实体框架附加:存在具有相同密钥的对象

时间:2014-03-18 03:49:30

标签: c# .net entity

我正在构建一个Windows窗体应用程序,并且我使用多个DBContext实例(每个业务层调用大多数一个)。

经过几天处理问题(插入新实体时,他们提到的实体被添加为新实体,而不是使用现有实体),我发现问题是我必须附加现有实体在上下文中。

大约2个小时的一切都很好,当我在附加时遇到错误:具有相同键的实体存在于上下文中。

我在附加之前尝试过测试(类似于每种实体类型的方法):

    private void attachIfNeeded(POCO.Program myObject, myContext context)
    {
    if (!context.Set<POCO.Program>().Local.Any(e => e.ID == myObject.ID))
        {
            context.programs.Attach(myObject);
            return true;
        }
        else
        {
            myObject = context.Set<POCO.Program>().Local.Single(e => e.ID == myObject.ID);
            return false;
        }
}

但测试返回false,但在附加时仍然失败。

所以基本上,如果我不附加,它将添加一个新实体,而不是使用现有(和预期)实体。如果我确实附上了,那我就错了。

我环顾四周(现在整整一天这样做)我实际上(想想我)知道问题是什么:

我尝试添加的实体具有多个关系,其他实体可以通过多个路径到达。这会导致问题吗?

请帮助解决这个问题,那里的解决方案对我来说毫无意义,并且没有工作。

我真的很接近我将尝试捕捉附加声明并完成它。但我会讨厌这样做。

以下是我的实体(并非所有实体,但这应该足够了):

 public class Word
{
    [Key]
    public int ID {get;set;}

    [Required]
    public string word { get; set; }


    public WordCategories category { get; set; }

    public Word parent {get;set;}

    public List<Unit> units { get; set; }

    public Program program { get; set; }

    public List<Lesson> lessons { get; set; }

    public Word()
    {
        units = new List<Unit>();
        lessons = new List<Lesson>();
    }
}
public class Unit
{
    [Key ]
    public int ID { get; set; }

    [Required]
    public string name { get; set; }

    public string description { get; set; }

    public List<Lesson> lessons { get; set; }

    public Program program {get;set;}

    public List<Word> words { get; set; }

    public Unit()
    {
        lessons=new List<Lesson>();
        words = new List<Word>();
    }

}

这是我调用attach方法的地方。第一次附加时会抛出错误:

public int addWords(List<POCO.Word > words,int programID, int unitID,int lessonID)
     {
         CourseHelperDBContext context = getcontext();

         int result;



        foreach(POCO.Word a in words)
        {
            foreach (POCO.Unit b in a.units)
                attachIfNeeded(b, context);
            foreach(POCO.Lesson c in a.lessons )
                attachIfNeeded(c,context);
            attachIfNeeded(a.program,context);
            if (a.parent != null)
                attachIfNeeded(a.parent,context);
        }

         context.words.AddRange(words);
         result = context.SaveChanges();
         return result;

     }

我无法相信我有这么多问题。我只想存储这些实体,添加一些(我还没有达到改变它们的程度)并保存它们。

到目前为止,我已经想过:

  1. 有些词是新的,有些是改变的,有些是改变的(大多是父母属性);
  2. 所有单位都存在,程序和课程也是如此(所以我需要附上它们);
  3. 对象图包含多个实体路径,其中一些存在,其中一些是新的;
  4. 我正在为每个请求使用新的上下文。当我一直使用相同的问题时,我遇到了其他问题。我找到了指向这种模式的解决方案,我觉得它没问题,因为那是你在ASP MVC项目中做的事情。
  5. 所有这些都可能导致问题,但我不知道哪些以及如何解决这些问题。

    我想我可以通过一次添加一个单词,每次都拉出程序,课程和单元来完成这项工作......但这意味着要多次往返DB。这不可能。

4 个答案:

答案 0 :(得分:3)

经过一段时间回到这个问题,在这种情况下的问题是我需要检索我的关系中存在的实体。

解决方案既不附加(因为如果实体已经附加就会失败)也不会添加(因为它已经存在于数据库中)。

我应该做的是检索与我正在添加的实体相关的每个实体。

这有助于: Entity Framework creating new entity with relationship to existing entity, results in attempt to create new copy of the existing entity

答案 1 :(得分:1)

附加实体后,尝试将实体状态设置为已修改。

context.programs.Attach(myObject);
context.Entry(myObject).State = EntityState.Modified;

我认为你的测试逻辑存在错误。

如果数据库中不存在实体,则应添加而不是附加。如果代码在真正添加时无法找到,那么您的代码就会附加。

添加新实体的代码(创建/插入)

context.Set<T>.Add(entity);

附加实体的代码(更新)

context.Set<T>.Attach(entity);
context.Entry(entity).State = EntityState.Modified;

如果你的代码在第一次附加时失败,那就是attachIfNeeded(b,context); ?我不认为你已经向我们展示了这个代码。

答案 2 :(得分:1)

与我分享我的经验,但有一个例外。 首先,这是我的代码:

        public void UpdateBulk(IEnumerable<Position> pDokumentPosition, DbDal pCtx)
        {
                foreach (Position vPos in pDokumentPosition)
                {
                    vPos.LastDateChanged = DateTime.Now;
                    pCtx.Entry(vPos).State = EntityState.Modified;
                 }
                pCtx.SaveChanges();
        }

我在EntityState.Modified行上遇到了相同的异常。

在我的情况下,问题是,将vPos状态设置为Modifyed时,所有相关对象(vPos.Document和vPos.Produkt)都以不变的状态加载到上下文中。 在foreach的第一步中,它不会引起任何异常,仅在第二步中,因为例如。相关的Dokument实体已经加载到内存/上下文中(因此,Dokument的key属性也是如此)。

我该如何解决? (可能不是最好的解决方案):

我在每一步中都用以下代码分离相关实体:

        if (vPos.Dokument != null)
        {
            pCtx.Entry(vPos.Dokument).State = EntityState.Detached;
        }

        if (vPos.Produkt!=null)
        {
            pCtx.Entry(vPos.Produkt).State = EntityState.Detached;
        }

如果您有更好的解决方案,我很期待...

答案 3 :(得分:0)

你可以试试这个

context.words.Add(words);
result=context.SaveChanges();
相关问题