Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
270 views
in Technique[技术] by (71.8m points)

c# - Is it really impossible to update child collection in EF out of the box (aka non-hacky way)?

Let's say you have these classes in your entities.

public class Parent
{
    public int ParentID { get; set; }
    public virtual ICollection<Child> Children { get; set; }
}

public class Child
{
    public int ChildID { get; set; }
    public int ParentID { get; set; }
    public virtual Parent Parent { get; set; }
}

And you have a user interface to update the Parent along with its Children, meaning if the user add new Child then you have to insert, if the user edits an existing Child then you need to update, and if the user removes a Child then you have to delete. Now obviously if you use the following code

public void Update(Parent obj)
{
    _parent.Attach(obj);
    _dbContext.Entry(obj).State = EntityState.Modified;
    _dbContext.SaveChanges();
}

it won't be able to detect the changes inside the Child because EF cannot detect changes inside a Navigation Property.

I've been asking this question for like 4 times and get mixed answers. So is it actually possible to do this stuff without it getting complicated? This problem can fix the problem by separating the user interface between Parent and Child but I don't want to because merging both Child and Parent in one menu is pretty common in business application development and more user friendly.

UPDATE : I'm trying the solution below but it doesn't work.

public ActionResult(ParentViewModel model)
{
    var parentFromDB = context.Parent.Get(model.ParentID);

    if (parentFromDB != null)
    {
        parentFromDB.Childs = model.Childs;
    }

    context.SaveChanges();
}

Instead of detecting changes inside the Children, EF won't be able to tell what to do with old child. For example if parentFromDB has 3 children the first time I pull it from DB then I delete the 2nd and 3rd child. Then I'm getting The relationship could not be changed because one or more of the foreign-key properties is non-nullable when saving.

I believe this is what happened : The relationship could not be changed because one or more of the foreign-key properties is non-nullable

Which took me back to square one because in my scenario, I can't just fetch from the DB and update the entry and call SaveChanges.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

because EF cannot detect changes inside Navigation Property

This seems to be a somewhat distorted description of the fact that _dbContext.Entry(obj).State = EntityState.Modified doesn't mark navigaton properties as modified.

Of course EF tracks changes in navigation properties. It tracks changes in properties and associations of all entities that are attached to a context. Therefore, the answer to your question, now positively stated...

Is it possible to update child collection in EF out of the box

... is: yes.

The only thing is: you don't do it out of the box.

The "out of the box" way to update any entity, whether it be a parent or a child in some collection is:

  • Fetch entities from the database.
  • Modify their properties or add/remove elements to their collections
  • Call SaveChanges().

That's all. Ef tracks the changes and you never set entity States explicitly.

However, in a disconnected (n-tier) scenario, this gets more complicated. We serialize and deserialize entities, so there can't be any context that tracks their changes. If we want to store the entities in the database, now it's our task to make EF know the changes. There are basically two ways to do this:

  • Set the states manually, based on what we know about the entities (like: a primary key > 0 means that they exist and should be updated)
  • Paint the state: retrieve the entities from the database and re-apply the changes from the deserialized entities to them.

When it comes to associations, we always have to paint the state. We have to get the current entities from the database and determine which children were added/deleted. There's no way to infer this from the deserialized object graph itself.

There various ways to alleviate this boring and elaborate task of painting the state, but that's beyond the scope of this Q&A. Some references:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...