首页 > 解决方案 > EntityFrameworkCore 插入现有的相关项

问题描述

考虑

 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;}
 }

我现在使用实体框架核心将 ItemTag 添加到 Item。这工作得很好。

现在我将第二个 ItemTag 添加到同一个项目。保存时,传递整个对象,包括现有的相关ItemTags。然后 EF 尝试插入现有的 ItemTag 失败,但出现“无法将值插入标识列...”的异常

那么如何防止现有对象被插入呢?

我的解决方法是遍历 ItemTags,并将任何具有 Id 的内容设置为 EntityState.Unchanged 以强制它不保存它。但似乎不需要这样的解决方法。

这是保存项目的代码:

//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();

标签: asp.net-coreentity-framework-coreasp.net-core-2.1entity-framework-core-2.1

解决方案


AutoMapper 不适合回溯到持久性或域模型。AutoMapper 的作者表示

这也不适用于 EF (Core)更改跟踪的方式。

根据从数据库加载的实体完成更改跟踪。当您更改实体值时,其状态将从 更改UnchangedModified并将导致更新方法。

当您加载一个没有项目关系的实体(使用急切加载)时,当您添加一个实体时,它会被标记为Added,从而在保存时插入。

你也有可能AttachDetach实体。

从链接的博客文章:

Rowan Miller 在 GitHub 问题 (bit.ly/295goxw) 中总结了新行为:

添加:添加尚未跟踪的每个可访问实体。

附加:附加每个可达实体,除非可达实体具有存储生成的键且未分配键值;这些将被标记为已添加。

更新:与附加相同,但实体被标记为已修改。

删除:与附加相同,然后将根标记为已删除。因为级联删除现在发生在 SaveChanges 上,这允许级联规则稍后流向实体。

因此,您必须附加您的项目(如果您只想添加带有主键的新项目)或调用Update/UpdateRangeDbSet更新带有键的项目并添加没有的项目。

或者通过设置/更改它的跟踪状态来自己处理它,就像你已经做的那样。但请记住,Unchanged不会更新传递的实体。


推荐阅读