LINQ to SQL SubmitChanges() Inserts two Database Rows and one Child Row - linq-to-sql

I have this going me crazy,
I'm attaching a List with 1 Customer and 1 Address child record row.
Everything seems OK while debugging. 1 customer Row and 1 Address Row should inserted.
But instead I get 2 Customer Records and 1 Address Row.
I don't know why. When Attaching and looping inside the List only 1 record seen.
Any points?
[EDITED]
Code Attached:
public bool InsertUpdateCustomers(List<Customer> customerList, List<Customer> originalCustomers)
{
using (DbContext db = new DbContext(DbContext.ConnectionString))
{
db.Log = Console.Out;
List<Customer> customerCloned = new List<Customer>();
customerList.ForEach(p => customerCloned.Add(p.CloneObjectGraph()));
customerCloned.ForEach(p => p.Address =
customerList.Where(pe => pe.Id == p.Id).Single().Address.CloneObjectGraph());
customerCloned.ForEach(p =>
{
if (p.Id > 0)
{
db.Customer.Attach(p,
originalCustomers.Single(
x => x.Id == p.Id));
db.Address.Attach(p.Address,
originalCustomers.Single(
x => p.AddressId== x.AddressId).
Address);
}
});
customerCloned.ForEach(p =>
{
if (p.Id == 0)
db.Customer.InsertOnSubmit(p);
});
try
{
db.SubmitChanges(ConflictMode.ContinueOnConflict);
return true;
}
catch (Exception ex)
{
return false;
}
}
}
I have checked the Log in the output and I see indeed 2 Inserts in the table.
I don't see nothing about the Address, but inserts correctly.
It could be the foreign key problem i don't get it.

I guess you've solved this for now but I ran into a similar issue and wanted to report back my understanding of this issue for future users.
The issue, I believe, is that you are using an existing list of Customer objects retrieved from the DB using a particular DataContext. You are then creating a new DataContext in your method and with this new DataContext, you are attaching an Address object.
This Address object (assuming has a foreign key relation with Customer) creates a new Customer object in the DB since the DataContext for which SubmitChanges is called, the originalCustomer is also treated as a new record.
In other words, to avoid these problems, you must re-use the existing DataContext using which the originalCustomer List was fetched so that inserting the child record of Address doesn't trigger an entry into the parent table.
Hope this helps.

Related

Invalid object graph in Linq to SQL

I have a GiftCards table in my DBML that has a related property called Audit. The Audits are stored in a separate table. Each Audit has a related Person associated to it. There is also a Persons table. The relationships are set up and are valid in my DBML.
The problem is that when I instantiate a new Gift Card I also create a new related Audit in the OnCreated() method. But at the same time, I also create a related Person when I instantiate a new Audit. The Person is the current user. Actually the Audit's OnCreated method checks if the user already exists.
The problem is that when I instantiate a new gift Card, it also creates an associated Audit, which is fine, and the Audit creates an associated Person. But the Person already exists in the database. When I look at the data context's GetChangeSet(), it shows 3 inserts. The Persion should not show as an insert because he already exists in the database.
Here is how I implemented this. It is an MVC application where the Controller receives a gift card:
[HttpPost]
public ActionResult Save(GiftCardViewModel giftCard)
{
if (ModelState.IsValid)
{
GiftCard gc = GiftCardViewModel.Build(giftCard);
repository.InsertOrUpdate(gc);
repository.Save();
return View("Consult", new GiftCardViewModel(repository.Find(gc.GiftCardID)));
}
else
SetupContext();
return View("_Form", giftCard);
}
The Gift Card has:
partial class GiftCard
{
partial void OnCreated()
{
// Set up default audit.
this.Audit = new Audit();
}
}
The Audit class has:
partial void OnCreated()
{
// Setup timestamp
this.Timestamp = DateTime.Now;
this.Person = Person.GetPerson(Membership.GetUser().UserName);
}
And finally, my Person class has:
public static Person GetPerson(String username)
{
using (GiftCardDBDataContext database = new GiftCardDBDataContext())
{
// Try to get the person from database
Person person = database.Persons.SingleOrDefault(personData => SqlMethods.Like(personData.Username, username));
if (person == null)
{
person = new Person()
{
Username = username,
FullName = "Full name TBD"
};
database.Persons.InsertOnSubmit(person);
database.SubmitChanges();
}
// Return person data
return person;
}
}
When I create a new gift card, I always get an error saying that it's attempting to insert a duplicate person in the Persons table. I don't understand because my static class specifically checks if the Person already exists, if yes, I return the Person and I don't create a new one. Yet, the GetChangeSet() shows three inserts including the Person, which is wrong.
What am I doing wrong here?
I believe your issue here is that you're using multiple contexts. You have one being created by your repository, and another is created in the static method on your Person object. You also aren't making any effort to attach the Person created/retrieved from the other context to the context of your Audit class.
You should look at a single unit of work, a single DataContext class, and perform all your work in that.

LINQ To SQL does not work when adding new object

I use the following code to insert a new record to my Users table:
public bool CreateUser(User obj)
{
obj.Id = Guid.NewGuid();
using (_db = new CMSDataContext())
{
obj.SiteId = SiteID;
_db.Users.InsertOnSubmit(obj);
_db.SubmitChanges();
}
return true;
}
I do not get any errors, and everything seems fine. I can read a record from database with same DataContext. But after the above method runs completely, I see nothing new in my Users table. Why?
Is the id column truly a PK in the sql server database?

Help with LinqtoSql

Im using the Repository pattern and I want to write a method that receives a role and returns an Iqueryable of the users that belong to that role. (Im not sure if the right way would be to receive the role object or the role_id... in any case, how can I do this?? I dont like the query structure, I prefer the method structure of linq.
users and roles is many to many with a users_roles join table.
private ClasesDataContext db = new ClasesDataContext();
public IQueryable GetByRole(Role role)
{
return db.Users.Where();
}
Maybe try something like:
public IQueryable<User> GetByRoleId(Role role) {
return db.UsersRoleJoinTable.Where(ur => ur.Role == role).select(ur => ur.User);
}
Where UsersRoleJoinTable is your many-to-many join table.
Hope it helps.
Update: the select(ur => ur.User) is telling linq that for every row returned by "db.UsersRoleJoinTable.Where(ur => ur.Role == role)" we want to get the user associated with the UsersRoleJoinTable object. If you wanted a list of user ids instead, you could tell linq to select only user.id by doing select(ur => ur.id). Think of linq's select as a some sort of "for every row do this and put it in the list returned instead of the original row"
There is one downside to this approach tho, I believe in this case Linq is generating the sql to get the rows from the Join table (UsersRoleJoinTable) and then for every row returned, is executing another query to look up the User. I might be wrong on this, so to check the SQL generated by Linq do:
string sql_query = db.UsersRoleJoinTable.Where(ur => ur.Role == role).select(ur => u.User).ToString();
and then print the value of sql_query or watch it in debug mode. If Linq is in fact doing multiple queries, then I think the best solution is to create a view or stored procedure in SQL Server to get the users associated with the role and then add the view or stored procedure to Visual Studio designer so that you can call the view like:
db.GetUsers(role_id) //if using a GetUsers stored procedure
or
db.UsersByRoleView.where(ur => ur.role_id == passed_role_id) //if using a UsersByRoleView view
If you have an instance of the Role object
public IQueryable<User> GetByRole(Role role) {
return db.Users.Where(u => u.Role == role);
}
would work.
If you don't but just know the Id or some other property of the role something like this might be better.
public IQueryable<User> GetByRoleId(int roleId) {
return db.Users.Where(u => u.Role.Id == roleId);
}

What's the best way to save a one-to-many relationship in Linq2Sql?

I'm trying to figure out the best way to save a simple one-to-many relationship in Linq2Sql.
Lets assume we have the following POCO model (pseduo code btw):
Person has zero to many Vechicles.
class Person
{
IList<Vehicle> Vehicle;
}
class Vehicle
{
string Name;
string Colour;
}
Now, when i save a Person, i pass that poco object to the repository code (which happens to be L2S). I can save the person object fine. I usually do this.
using (Db db = new Db())
{
var newPerson = db.People.SingleOrDefault(p => p.Id == person.Id) ?? new SqlContext.Person();
// Left to right stuff.
newPerson.Name = person.Name;
newPerson.Age = person.Age;
if (newPerson.Id <= 0)
db.People.InsertOnSubmit(newPerson);
db.SubmitChanges();
}
i'm not sure where and how i should handle the list of vehicles the person might have? any suggestions?
using (Db db = new Db())
{
var newPerson = db.People.SingleOrDefault(p => p.Id == person.Id) ?? new SqlContext.Person();
// Left to right stuff.
newPerson.Name = person.Name;
newPerson.Age = person.Age;
// add vehicles.
Vehicle firstV = new Vehicle();
firstV.Name = "some name";
firstV.Person = newPerson; // need to do this to set the person Id on the vehicle.
newPerson.Vehicle.Add(firstV);
// now when you save the Person it should save the Vehicle list
// if you set Cascade save update on the list. (not sure how to do that in L2S
if (newPerson.Id <= 0)
db.People.InsertOnSubmit(newPerson);
db.SubmitChanges();
}
Now you may choose to construct the list of vehicles at another level , with the data that's coming from the interface.
But you need to remember that it's not enough to add the Vehicle to the list on the Person object , you also need to set the vehicles Person property to the person that has the vehicles.
Observation I'm not sure about this but when you do db.People.SingleOrDefault you might be loading the whole People table in memory . That's not something you want to do. Corrected by Slace in the comments.
All you need to do is ensure that there are the appropriate relationships set up within the database.
If your Vehicle table has a PersonId and there is a foreign key between them when you add them to the DBML Linq to SQL will detect that there is a relationship between them and create a Table<T> representation of the relationship.

Problem with Linq2Sql Many-to-Many relationship & Inserting new objects

i'm trying to do a simple linq 2 sql many-to-many, insert some data, operation.
here's the stock Northwind model representing a many to many:
alt text http://www.iaingalloway.com/images/linq-detail.jpg
Now what i'm trying to do is insert a new order and if the product doesn't exist, then insert that at the same time, within the same transaction. The error i'm getting is:
System.Data.Linq.DuplicateKeyException: Cannot add an entity with a
key that is already in use.
So this is my (pseduo) code:
using (SqlContext db = new SqlContext())
{
// Get existing or create a new instance.
Order newOrder = GetOrder(order.Id) ?? new Order();
// Left to right stuff.
newOrder.Foo = order.Foo;
// Now associate this new order to a product (which might not exist).
if (!order.ProductList.IsNullOrEmpty())
{
// We have some products...
IList<Order_Detail> orderDetailList = new List<Order_Detail>();
foreach(Models.Product product in order.ProductList)
{
// Associate each product to the a new order_detail.
orderDetailList.Add(new Order_Detail
{
Product = new SqlContext.Product
{
Foo = product.Foo
}
});
}
// Now associate all the order_details to this order.
newOrder.Order_Details.AddRange(orderDetailList);
if (newOrder.Id <= 0)
db.InsertOnSubmit(newOrder);
db.SubmitChanges(); // <-- exception throw here.
}
}
I'm assuming i need to save the products first before i try and save the order? I'm so confused :(
// Associate each product to the a new order_detail.
orderDetailList.Add(new Order_Detail
{
Product = new SqlContext.Product
{
Foo = product.Foo
}
});
One thing that is wrong here, is that you create a new product to set on your Order_Detail.Product property. Instead , you should take the product that's comming from the database and set it on the property.
I'm not sure what order.ProductList has inside - if these products are loaded from the database then you should set them directly to your Order_Detail.Product instead of doing new SqlContext.Product.
#jfar L2S does support many-to-many relationships , you just can't have a property Products on your Order ( in this case this is actually a good thing because OrderDetails has Quantity and other properties).
Many to Many relationships aren't supported in Linq2Sql. :(
There are a couple of workarounds:
http://www.iaingalloway.com/many-to-many-relationships-in-linq-to-sql
http://blogs.msdn.com/mitsu/archive/2008/03/19/how-to-implement-a-many-to-many-relationship-using-linq-to-sql-part-ii-add-remove-support.aspx
Weird that the picture of your db schema is the same as one of the articles...