简体   繁体   中英

Entity Framework - Updating entity with added/modified child and grandchild entities

I am not sure if I am doing this the right way or not, so need advice.

I have an entity, this entity has a child collection, and each child entity has another child collection. Something like this (simplified example)

public MyEntity() {
    public long Id { get; set; }
    public ICollection<MyChild> Children { get; set; }
}

public MyChild() {
    public long Id { get; set; }
    public long MyEntityId { get; set; }
    public MyEntity MyEntity { get; set; }
    public ICollection<MyGrandChild> Children { get; set; }
}

public MyGrandChild() {
    public long Id { get; set; }
    public long MyChildId { get; set; }
    public MyChild MyChild { get; set; }
    public string Name { get; set; }
}

Now in our application, the user retrieves this entity from our webApi into an angularJs application. The user then updates the entity (and sub entities) and passes the entity back to the webApi. I am using models to pass the objects from my webApi to the angularJs application, and they look something like this.

public MyEntityModel() {
    public long Id { get; set; }
    public ICollection<MyChildModel> Children { get; set; }
}

public MyChildModel() {
    public long Id { get; set; }
    public ICollection<MyGrandChildModel> Children { get; set; }
}

public MyGrandChildModel() {
    public long Id { get; set; }
    public string Name { get; set; }
}

Once the models are passed back to the webApi, I use Auto Mapper to convert them back to entity objects.

Now the bit I am confused about, i now pass the object to my service layer, my method looks similar to this

public Task<int> UpdateAsync(MyEntity updated) {
    _context.Entry(updated).State = EntityState.Modified;

    return _context.SaveChangesAsync();
}

If I add a new MyChild or MyGrandChild object to MyEntity after MyEntity exists or update MyChild or MyGrandChild object then the changes are not committed to the database? I changed my UpdateAsync method to this, but is this really needed?

public Task<int> UpdateAsync(MyEntity updated) {
    _context.Entry(updated).State = EntityState.Modified;

    foreach (var child in updated.Children) {
        if (child.Id == 0) {
            _context.Entry(child).State = EntityState.Added;
        } else {
            _context.Entry(child).State = EntityState.Modified;
        }

        foreach (var grand in child.Children) {
            if (grand.Id == 0) {
                _context.Entry(grand).State = EntityState.Added;
            } else {
                _context.Entry(grand).State = EntityState.Modified;
            }
        }
    }

    return _context.SaveChangesAsync();
}

Do I really have to loop through each collection, and sub collection, check if the id equals 0 and set its state accordingly?

Yes, you have to do that.

When you do all the work inside a DbContext scope it takes care to track al the changes happening in the entities, and you have to do nothing to let the DbContext know whathas changed.

But, in multi-layered applications, when you move the entities between layers they cannot be kept inside a DbContext scope, so you're responsible for tracking the cahnges.

Julie Lerman recommends implementing an interface to track the status of each entity. This interface has a property that keeps the entity status. This is modified on the client side and visited on the server to set each entity status: get the entities on the server side, attach them to the context, and modify its status according to the tracking interface property. (I can't find the reference, but it's covered in her Programming Entity Framework book, and in one of her Pluralsight courses).

Trackable Entities can also be of your interest.

If you want this to happen "automagically" you can use Breeze . This let you easyly expose an EF model on the client side, using JavaScript code. This code is able to track the changes (and do many other things like validating) in the client side, and send them back to the server to update the database. It's quite easy to get started. Basically you need to install the NuGet package for the server to implement a Breeze controllers, which is done with very little lines of code, and the NuGet package for the client, that implements the JavaScript code. It's advisable to use some MVVM JavaScript library, like Knockout or AngularJS , because the changes will be automatically tracked by subscribing to the observables created by these libraries.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM