Question

I have a parent entity

foo which exists in the db, I have a property bar on this entity (One to many relation).

Foo is detached because its deserialized using WebApi, so I do this to foo

context.Foos.AddOrUpdate(foo);

Even If there is a new bar reference attached to it, it wont get saved, however it does work like this for another relation we have which is a Many to many relation. If I add a new entity to that collection it will be saved to its table and also a row is added to the relation table.

If I do context.Bars.AddOrUpdate(foo.Bar); before calling context.Foos.AddOrUpdate(foo); it will save the new bar correctly to the bar table, but it wont add the correct barId to the foo table

@Yuliam Chandra

If i understand your answer correctly (I think you are mixing up bars and foos in your answer) this should work?

var foo = new Foo();
foo.FooId = 524 //Existing foo;

foo.Bar = new Bar(); //Completely new bar    
db.Foos.AddOrUpdate(foo);
db.SaveChanges();

But it does not

Was it helpful?

Solution

Working with disconnected objects needs extra code.

If you are working with disconnected objects you must manually manage the synchronization.

Source

If Bar is an existing entity you need to attach it first, so Foo will be added as Bar's children.

if (foo.Bar.Id != 0)
{
    context.Bars.Attach(foo.Bar);
    context.Foos.AddOrUpdate(foo);
}

The example of above code is similar with Course (Foo) and Department (Bar) example in this article.

But if Bar is a new entity you just need to add Foo, then Bar will also be added.

else
{
    context.Foos.Add(foo);
}

Some other variety can be checked on my answer.

update

Before explaining further, I'd like show identical code.

  • db.Set<T>().Add(instance) equals to db.Entry(instance).State = EntityState.Added;
  • db.Set<T>().Attach(instance) equals to db.Entry(instance).State = EntityState.Unchanged;

The previous answer explained about two conditions.

  • New Foo and existing Bar

    context.Bars.Attach(foo.Bar);

    context.Foos.AddOrUpdate(foo);

  • New Foo and new Bar

    context.Foos.Add(foo);

Special Added

Added has special condition, once an entity is marked as Added. all the entities in the graph will be marked as Added too, even if any reference entity is an existing object in the database. If we have this code, Foo and Bar will be added.

var foo = new Foo (); // new foo
foo.Bar = new Bar { BarId = 123 }; // existing bar
db.Set<Foo>().Add(foo);
db.SaveChanges();

To prevent that from happening, Bar needs to be attached first.

var foo = new Foo (); // new foo
foo.Bar = new Bar { BarId = 123 }; // existing bar
db.Set<Bar>().Attach(bar);
db.Set<Foo>().Add(foo);
db.SaveChanges();

Check Julie Lerman article for more complete explanation.

The reason it happens is that when you use the DbSet.Add method (that is, Screencasts.Add), not only is the state of the root entity marked “Added,” but everything in the graph that the context was not previously aware of is marked Added as well. Even though the developer may be aware that the Topic has an existing Id value, Entity Framework honors its EntityState (Added) and creates an Insert database command for the Topic, regardless of the existing Id.

Adjust Explanation Based On Your Update

And your updated question is about existing Foo and new Bar. Using your code, the result will not add new Bar and existing Foo will not make a relationship with new Bar.

var foo = new Foo();
foo.FooId = 524 //Existing foo;

foo.Bar = new Bar(); //Completely new bar    
db.Foos.AddOrUpdate(foo);
db.SaveChanges();

If we manually add the Bar together with AddOrUpdate(foo), the result is still not as expected. The new Bar will be added, but Foo will not have relationship with the new Bar.

db.Bars.Add(foo.Bar);
db.Foos.AddOrUpdate(foo);

The behavior of AddOrUpdate is inconsistent.

Solution

If you are working with disconnected objects you must manually manage the synchronization.

This code should work in all conditions.

  • New Foo and New Bar
  • New Foo and Existing Bar
  • Existing Foo and New Bar
  • Existing Foo and Existing Bar

This code depends on the id (id = 0 is a new entity).

db.Entry(foo).State = 
       foo.FooId == 0 ? EntityState.Added : EntityState.Modified;
if (foo.Bar != null)
{
    db.Entry(foo.Bar).State = 
       foo.Bar.BarId == 0 ? EntityState.Added : EntityState.Modified;
                                                             ^^^
    // If you don't want to change the Bar while creating a relationship between
    // Foo and with existing Bar, you can change 
    // `EntityState.Modified` with `EntityState.Unchanged`
}
db.SaveChanges();

Related to AddOrUpdate, I think there is an open issue that can be found here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top