【问题标题】:`Attaching an entity of type failed...` exception while trying to update a class in EF and with a generic repository尝试使用通用存储库更新 EF 中的类时,“附加类型的实体失败...”异常
【发布时间】:2015-05-20 06:10:58
【问题描述】:

我正在使用实体框架。我想加载一个实体,对其进行编辑,然后将更改保存回数据库中。但是无论我编辑的是外键属性还是简单属性,EF都会给我以下错误:

附加“ClassX”类型的实体失败,因为同一类型的另一个实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“附加”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“已添加”实体状态来跟踪图形,然后将非新实体的状态设置为“未更改”或“已修改”。

请注意,ClassX 不是我要更新的类的直接虚拟属性,而是我的类具有导航属性的其他一些类中的虚拟属性。

我已阅读 some related 问题。但我并没有真正明白如何将它们应用于我自己的问题,因为我使用的是下面发布的通用存储库。

public class GenericRepository<T>  where T : class
{
    private EFDbContext context = new EFDbContext();
    public IEnumerable<T> GetAll()
    {
        return context.Set<T>();
    }
    public void Insert(T entity)
    {
        context.Set<T>().Add(entity);
        context.SaveChanges();
    }
    public void Update(T entity)
    {
        context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
        context.SaveChanges();
    }
 //removed for brevity
}

我遇到过与虚拟属性相关的another problem,建议我使用ViewModels 和对象到对象的映射。

据我所知,有 3 个选项:

  1. 使用ViewModels 和对象到对象的映射。这个我就不说了,因为o2o映射库有很多bug,真的很痛苦。
  2. 不知何故使用reference。但我不能这样做,因为存储库是通用的。也许我应该为此使用反射 API?
  3. 删除所有虚拟属性。这实际上是一种选择,因为他们制造的问题多于解决的问题。

谁能解释一下为什么会出现这个问题以及解决这个问题的最简单方法是什么?

【问题讨论】:

标签: c# entity-framework ef-code-first entity-framework-6


【解决方案1】:

当您将实体的State 设置为Modified 时,它还会将所有子实体(导航属性引用的实体)附加到State == EntityState.Unchanged。如果您的上下文已经有具有相同键的实体,则会引发该错误。

如果您希望忽略这些实体,我可以想到几个选项:

  1. Update 中创建一个新的数据上下文,不要担心子实体,因为使用EntityState.Unchanged,当您调用SaveChanges 时,它们将被忽略。如果您使用某种存储库模式,这可能没有意义。
  2. 在设置State = EntityState.Modified 之前,浏览您不想附加并设置为null 的导航属性
  3. 设置State = EntityState.Modified后,对于要忽略的子实体,设置State = EntityState.Detached

编辑

首先弄清楚为什么上下文会以具有相同键的多个子实体结束也是很好的。

【讨论】:

  • 感谢您的评论。由于我的存储库是通用的,所以选项二和三需要一些反射 API 才能使用。选项#1怎么样?你的意思是我应该使用这样的东西:new efdbcontext().entry(entity).state = EntityState.Modified; 并忽略原始上下文?
  • 导航属性也导致two更多issues。您认为最好和最简单的方法是什么?
  • 好吧,如果我们回顾一下大局,您的实现方式可能有问题。 #1 对于您的设计模式可能真的没有意义,尽管我对您想要做的事情并不熟悉。了解有关此 GenericRepository 在其生命周期中的使用方式的更多信息可能会有所帮助。
  • 我没有工作单元模式、服务或类似的东西,恐怕我现在可以更改应用程序。所以在控制器中,我将存储库用于不同的类。请注意,我没有多个键,我不知道这个错误来自哪里。
  • 您是否使用GenericRepository 的同一个实例来更新/插入多个实体,并且这些实体碰巧引用了相同类型的不同对象(例如ClassX)并且具有相同的主键?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
  • 2017-08-26
  • 2017-11-07
  • 2020-03-27
  • 1970-01-01
相关资源
最近更新 更多