Reputation: 17554
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
Upvotes: 2
Views: 1265
Reputation: 14640
Working with disconnected objects needs extra code.
If you are working with disconnected objects you must manually manage the synchronization.
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.
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);
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.
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.
If you are working with disconnected objects you must manually manage the synchronization.
This code should work in all conditions.
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.
Upvotes: 2