Update "one to many" relationships with separate associations using Entity Framework, Code First.

associations code-first dbcontext entity-framework


I spent much too much time trying to solve the problem in the situation below. What seemed to be a straightforward situation turned out to be somewhat challenging. The inquiry is:

How can I change the end of an existing "many to one" connection in a "detached" situation using Entity Framework 4.1 (Code First technique) and "Independent associations"? ( Asp.Net in my case).

The example:

Although it would have been possible to use ForeignKey connections in place of Independent Associations, I opted to forgo this option in favor of keeping my Pocos' ForeignKey implementation.

One or more targets are assigned to a customer:

    public class Customer:Person
    public string Number { get; set; }
    public string NameContactPerson { get; set; }
    private ICollection<Target> _targets;

    // Independent Association
    public virtual ICollection<Target> Targets
        get { return _targets ?? (_targets = new Collection<Target>()); }
        set { _targets = value; }

One Customer makes up a Target.

    public class Target:EntityBase
    public string Name { get; set; }
    public string Description { get; set; }
    public string Note { get; set; }
    public virtual Address Address { get; set; }
    public virtual Customer Customer { get; set; }

Customer is a subclass of Person.

    public class Person:EntityBase
    public string Salutation { get; set; }
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set  ; }        
    public string Telephone1 { get; set; }
    public string Telephone2 { get; set; }
    public string Email { get; set; }        

    public virtual Address Address { get; set; }

The EntityBase class has the following typical properties:

    public abstract class EntityBase : INotifyPropertyChanged
    public EntityBase()
        CreateDate = DateTime.Now;
        ChangeDate = CreateDate;
        CreateUser = HttpContext.Current.User.Identity.Name;
        ChangeUser = CreateUser;
        PropertyChanged += EntityBase_PropertyChanged;

    public void EntityBase_PropertyChanged(object sender, PropertyChangedEventArgs e)
        if (Id != new Guid())
            ChangeDate = DateTime.Now;
            ChangeUser = HttpContext.Current.User.Identity.Name;

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, e);

    public event PropertyChangedEventHandler PropertyChanged;

    public Guid Id { get; set; }
    public DateTime CreateDate { get; set; }
    public DateTime? ChangeDate { get; set; }
    public string CreateUser { get; set; }
    public string ChangeUser { get; set; }

The Situation:

    public class TgrDbContext : DbContext
    public DbSet<Person> Persons { get; set; }
    public DbSet<Address> Addresses { get; set; }
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Target> Targets { get; set; }
    public DbSet<ReportRequest> ReportRequests { get; set; }

    // If OnModelCreating becomes to big, use "Model Configuration Classes"
    //(derived from EntityTypeConfiguration) instead
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
        modelBuilder.Entity<Person>().HasOptional(e => e.Address);            
        modelBuilder.Entity<Customer>().HasMany(c => c.Targets).WithRequired(t => t.Customer);            

    public static ObjectContext TgrObjectContext(TgrDbContext tgrDbContext)
        return ((IObjectContextAdapter)tgrDbContext).ObjectContext;
11/12/2015 10:03:26 PM

Accepted Answer

Because there are other answers to this issue, I waited for @Martin's response. Here is another one (it should work with DbContext API as well as it at least works with ObjectContext API):

// Existing customer
var customer = new Customer() { Id = customerId };
// Another existing customer
var customer2 = new Customer() { Id = customerId2 };

var target = new Target { ID = oldTargetId };
// Make connection between target and old customer
target.Customer = customer;

// Attach target with old customer
// Attach second customer
// Set customer to a new value on attached object (it will delete old relation and add new one)
target.Customer = customer2;

// Change target's state to Modified
context.Entry(target).State = EntityState.Modified;

Internal state model and state validations inside EF are the issue here. When there is no other entity in the deleted state, an entity in an unmodified or modified state with an obligatory connection (on multiple sides) cannot have an independent association in the added state. Modified states for association are completely forbidden.

8/7/2011 9:20:14 PM

Popular Answer

There is a ton of literature available on this subject, but I really appreciated Ladislav Mrnka's views on Stackoverflow. Additional information is available here: Here is a What has Entity Framework 4 added? and Entity Framework NTier Improvements

The customer allocated to a Target object in my project (Asp.Net Webforms) may be changed to another (already existing) Customer object at the user's discretion. An ObjectDataSource-bound FormView control executes this transaction. The BusinessLogic layer of the project receives communications from the ObjectDataSource, which then sends the transaction to a repository class for the Target object in the DataAccess layer. The Target object's Update function in the Repository class is represented as follows:

    public void UpdateTarget(Target target, Target origTarget)
            // It is not possible to handle updating one to many relationships (i.e. assign a 
            // different Customer to a Target) with "Independent Associations" in Code First.
            // (It is possible when using "ForeignKey Associations" instead of "Independent 
            // Associations" but this brings about a different set of problems.)
            // In order to update one to many relationships formed by "Independent Associations"
            // it is necessary to resort to using the ObjectContext class (derived from an 
            // instance of DbContext) and 'manually' update the relationship between Target and Customer. 

            // Get ObjectContext from DbContext - ((IObjectContextAdapter)tgrDbContext).ObjectContext;
            ObjectContext tgrObjectContext = TgrDbContext.TgrObjectContext(_tgrDbContext);

            // Attach the original origTarget and update it with the current values contained in target
            // This does NOT update changes that occurred in an "Independent Association"; if target
            // has a different Customer assigned than origTarget this will go unrecognized
            tgrObjectContext.AttachTo("Targets", origTarget);
            tgrObjectContext.ApplyCurrentValues("Targets", target);

            // This will take care of changes in an "Independent Association". A Customer has many
            // Targets but any Target has exactly one Customer. Therefore the order of the two
            // ChangeRelationshipState statements is important: Delete has to occur first, otherwise
            // Target would have temporarily two Customers assigned.
                o => o.Customer,

                o => o.Customer,

            // Commit
            tgrObjectContext.Refresh(RefreshMode.ClientWins, origTarget);
        catch (Exception)

This is true for the Target object's Update function. Surprisingly, adding a new Target object is much simpler to do. After correctly identifying the Customer end of the independent relationship, DbContext immediately commits the update to the database. The repository class's Insert function looks like this:

        public void InsertTarget(Target target)
        catch (Exception)

I'm hoping this may be helpful to someone working on a project like this. Please mention any issues you find with the above-described strategy in your comments. Thanks!

Related Questions


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow