【问题标题】:EntityFrameworkCore inserting existing related itemsEntityFrameworkCore 插入现有的相关项
【发布时间】:2018-11-19 10:52:44
【问题描述】:

考虑

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

【问题讨论】:

  • 简单修复:不要使用 AutoMapper。 AutoMapper 的作者多次表示它不适合这种映射。只有持久性模型 -> DTO 或 ViewModel 和/或域模型 -> ViewModel/DTO。永远不要反过来。 EF Core 通过引用跟踪实体,自动映射器创建新引用,因此问题
  • @Tseng,好的。我对此一无所知。所以我应该手动编写从输入到实际的映射?
  • 没有。 AM.Collection 可以为您添加对 AM 的支持。
  • @LucianBargaoanu,你能解释一下吗?也许是一个例子?
  • @LucianBargaoanu,似乎还不支持 EF Core:github.com/AutoMapper/AutoMapper.Collection.EFCore

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


【解决方案1】:

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

这也不能像 EF (Core) change tracking 那样运作良好。

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

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

您还可以使用AttachDetach 实体。

来自链接的博客文章:

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

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

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

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

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

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

您也可以通过设置/更改其跟踪状态来自行处理。但请记住,Unchanged 不会更新传递的实体。

【讨论】:

  • 感谢您的出色回答!当我们想要插入现有的相关项目时,您能否展示一下代码应该是什么样子?我的意思是你显示的colud 请为调用DbSet 上的Update/UpdateRange 编写代码以使用密钥更新那些并添加那些没有
【解决方案2】:

对于插入新的ItemTag,您可以直接插入ItemTag 表而不反映Item

如下定义你的模型:

public class ItemTag
{
    public int Id { get; set; }
    public int Name { get; set; }
    public int ItemId { get; set; }
    public virtual Item Item { get; set; }
}

尝试为ItemTag 设置ItemIdItem 插入一个新的ItemTag

如果您喜欢在ItemTag 中插入Item,您可以尝试JsonPatchDocument,它描述了如何操作模型。

尝试参考JSON Patch With ASP.net Core

对于JsonPatchDocument,您需要在客户端生成JsonPatchDocument&lt;Item&gt;。尝试参考ASP Core PatchDocument returning Invalid Input

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-26
    • 2018-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多