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

南楼画角 提交于 2019-11-30 18:23:24
Gert Arnold

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:

Its cozs your doing it weirdly.

This requires Lazy loading for getting childs (obviously modify for your usage)

//get parent

var parent = context.Parent.Where(x => x.Id == parentId).SingleOrDefault();

wrote a whole test method for you. (apply to your case)

EmailMessage(parent) is the parent and it has none or many EmailAttachment's(child's)

 [TestMethod]
    public void TestMethodParentChild()
    {
        using (var context = new MyContext())
        {
            //put some data in the Db which is linked
            //---------------------------------
            var emailMessage = new EmailMessage
            {
                FromEmailAddress = "sss",
                Message = "test",
                Content = "hiehdue",
                ReceivedDateTime = DateTime.Now,
                CreateOn = DateTime.Now
            };
            var emailAttachment = new EmailAttachment
            {
                EmailMessageId = 123,
                OrginalFileName = "samefilename",
                ContentLength = 3,
                File = new byte[123]
            };
            emailMessage.EmailAttachments.Add(emailAttachment);
            context.EmailMessages.Add(emailMessage);
            context.SaveChanges();
            //---------------------------------


            var firstEmail = context.EmailMessages.FirstOrDefault(x => x.Content == "hiehdue");
            if (firstEmail != null)
            {
                //change the parent if you want

                //foreach child change if you want
                foreach (var item in firstEmail.EmailAttachments)
                {
                    item.OrginalFileName = "I am the shit";
                }
            }
            context.SaveChanges();


        }
    }

Update

Do your AutoMappper Stuff... as you said in your comment.

Then when you are ready to save and you have it back as the correct types ie once which represent entitys(Db) then do this.

var modelParent= "Some auto mapper magic to get back to Db types."

var parent = context.Parent.FirstOrDefault(x => x.Id == modelParent.Id);
//use automapper here to update the parent again

if (parent != null)
{
  parent.Childs = modelParent.Childs;
}
//this will update all childs ie if its not in the new list from the return 
//it will automatically be deleted, if its new it will be added and if it
// exists it will be updated.
context.SaveChanges();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!