views:

125

answers:

2

I have a very general question about updating a record in Linq-to-SQL. Suppose, in my data model, I have a base record (Table - Person) with an m:m relationship to a set of Categories (Table - Category). Therefore, I have an associative table (PersonCategory) that has foreign keys to both a PersonID and CategoryID.

When I want to update a Person, I may have new PersonCategory records to add and may want to remove other PersonCategory records. What is the best practice for doing such a thing? Would I want to remove all records in Person.RelatedPersonCategories and then add new in? Is LINQ-to-SQL smart enough to negotiate what records in the PersonCategory table are actually being added, edited, or deleted (based on looking at its foreign key potentially)?

Thanks for any help!

A: 

From my experience you don't have to do anything special here. PersonCategory is just an entity, and you add or remove instances from the related table instance on a data context as you would for any other entity, via InsertOnSubmit() and DeleteOnSubmit(). Updates to existing PersonCategory objects are handled by the change tracking system.

You certainly don't need to remove all items first and then add new ones back in.

It gets more complex when you look at the model code - provided you have foreign keys set up correctly in the database, the generated model code should completely handle association management as well.

Eg,

  • Person will have an EntitySet<PersonCategory>

  • PersonCategory will have an EntityRef<Person> and EntityRef<Category>

  • Category will have an EntitySet<PersonCategory>.

Whenever changes are made to these fields, the associated objects are updated as well:

  • If I change the Person property on a PersonCategory to null, the code will also update the related EntitySet<PersonCategory> accordingly by removing that PersonCategory from the prior person's EntitySet<PersonCategory>

  • If I add a new PersonCategory to a person's EntitySet<PersonCategory>, the code will automatically set the Person property to the new parent Person as well.

So yes, the basic answer is LINQ to SQL is smart enough to handle this for you - as long as you are generating the model code from your database.

Sam
+2  A: 

As long as there is an active instance of the DataContext class to track changes, LINQ to SQL will happily insert/update/delete rows in an associated table everytime objects in the collection that maps the relationship in the model are modified, and the DataContext.SubmitChanges() method is called.

For example:

using (var db = new DataContext())
{
    var person = db.Persons.Where(p => p.Name == "Foo").SingleOrDefault();

    if (person != null)
    {
        // Inserts a new row in the 'PersonCategory' table
        // associated to the current 'Person'
        // and to the 'Category' with name 'Employee'
        person.PersonCategories.Add(new PersonCategory() { CategoryName = "Employee" });

        // Updates the 'CategoryName' column in the first row
        // of the 'PersonCategory' table associated to the current 'Person'
        person.PersonCategories(0).CategoryName = "Consultant";

        db.SubmitChanges();
    }
}

Things are a little different if you are making changes to the model objects in "disconnected" mode, that is when the DataContext instance that was used to initially create those objects no longer is around.

In this case insert/delete operations on associated tables will work just fine when the object having the modified collection is attached to a new DataContext with the Table(TEntity).Attach method, followed by DataContext.SubmitChanges().

However, modifications on any of the existing objects in the collection will not automatically be applied in the associated table. In order to do that, you must manually call the Table(TEntity).Attach method for each object in the collection.
Here is a quote from the MSDN documentation:

When a new entity is attached, deferred loaders for any child collections (for example, EntitySet collections of entities from associated tables) are initialized. When SubmitChanges is called, members of the child collections are put into an Unmodified state. To update members of a child collection, you must explicitly call Attach and specify that entity.

Here is a concrete example:

// The 'Person' object has been detached
// from the originating 'DataContext', which is now disposed
person.PersonCategories.Add(new PersonCategory() { CategoryName = "Employee" });
person.PersonCategories(0).CategoryName = "Consultant";

using (var db = new DataContext())
{
    // Will detect added and deleted objects
    // in the 'PersonCategory' collection        
    db.Person.Attach(person);

    // Required to detect and modifications
    // to existing objects in the 'PersonCategory' collection
    foreach (var personCategory in person.PersonCategories)
    {
        db.PersonCategory.Attach(personCategory);
    }

    db.SubmitChanges();
}
Enrico Campidoglio