EntityFrameworkCore inserting existing related items

asp.net-core asp.net-core-2.1 entity-framework-core entity-framework-core-2.1

Question

Consider

 public class Item
 {
    public int Id{get;set;}
    public string Name{get;set;}
    public List<ItemTag> ItemTags{get;set;}
 }

 public class ItemTag
 {
    public int Id{get;set;}
    public int Name{get;set;}
 }

I now use entity framework core to add an ItemTag to Item. This works just fine.

Now I add a second ItemTag to the same Item. When saving, the entire object is passed, including the existing related ItemTags. EF then tries to insert the existing ItemTag which fails with an exception of 'Cannot Insert a value into an Identity Column...'

So how do I prevent the existing object from getting Inserted?

My workaround is to loop through the ItemTags, and set any that have an Id to EntityState.Unchanged to force it not to save it. But it seems that such a workaround should not be required.

This is the code that save the item:

//Get the current item, so that only updated fields are saved to the database.
var item = await this.DbContext.Items.SingleAsync(a => a.Id == itemInput.Id);
item.UpdatedBy = this._applicationUserProvider.CurrentAppUserId;
item.Updated = DateTimeOffset.UtcNow;

//Use Automapper to map fields.
this._mapper.Map(itemInput, item);

//Workaround for issue.
foreach (var itemTag in item.ItemTags)
{
    var entry = this.DbContext.Entry(itemTag);
    if (itemTag.Id > 0)
    {
        entry.State = EntityState.Unchanged;
    }
}

await this.SaveChangesAsync();
1
2
11/18/2018 11:55:34 AM

Popular Answer

AutoMapper isn't suitable for backing back to persistence or domain models. The author of AutoMapper stated.

This also doesn't work well in the way EF (Core) does change tracking.

Change tracking is done, based on the entities that are loaded form the database. When you change an entities value its state changes from Unchanged to Modified and will result in an update method.

When you load an entity w/o the items relation (using eager loading), then when you add an entity it will me marked as Added, resulting in an insert when saved.

You also have the possibility to Attach and Detach entities.

From the linked blog post:

Rowan Miller summarized the new behavior in a GitHub issue (bit.ly/295goxw):

Add: Adds every reachable entity that isn’t already tracked.

Attach: Attaches every reachable entity, except where a reachable entity has a store-generated key and no key value is assigned; these will be marked as added.

Update: Same as Attach, but entities are marked as modified.

Remove: Same as Attach, and then mark the root as deleted. Because cascade delete now happens on SaveChanges, this allows cascade rules to flow to entities later on.

So you'll have to attach your items (if you only want to add new ones w/o a primary key) or call Update/UpdateRange on the DbSet to update the ones with a key and add the ones without.

Alternatively handle it yourself by setting/changing its tracked state as you already did. But keep in mind, Unchanged won't update the passed entities.

2
11/18/2018 12:37:54 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow