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

本文关键字:对象 存在 框架 实体 | 更新日期: 2023-09-27 17:50:39

我正在构建一个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项目上所做的。

所有这些都可能引起问题,但我不知道是哪些问题,也不知道如何解决。

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

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

过了一段时间后回到这个问题,这个例子中的问题是我需要检索存在于我的关系中的实体。

解决方案既不是附加(因为如果实体已经附加,它将失败)也不是添加(因为它已经存在于DB上)。

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

这帮助:实体框架创建与现有实体有关系的新实体,导致试图创建现有实体的新副本

添加实体后,尝试将实体状态设置为modified

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

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

如果数据库中不存在实体,应该添加而不是附加。你的代码是附加的,如果它不能找到一个实体,当它真的应该添加。

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

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

附加实体(Update)的代码

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

如果你的代码在第一个附加上失败,那将是attachIfNeeded(b,context);? 我想你没有给我们看这个的代码。

我也有同样的例外。首先,这是我的代码:

        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上得到了相同的异常。行修改。

在我的例子中,问题是,当将vPos状态设置为modified时,所有相关对象(vPos)都将被删除。文档和vpos . product)以未改变的状态加载到上下文中。在每一步前,它不做任何例外,只是在第二步,因为eg。相关的文档实体已经加载到内存/上下文中(所以文档的关键属性也是)。

我怎么解决它?(也许不是最好的解决方案):

我将每一步中的相关实体拆分为以下行:

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

如果你有更好的解决方案,我很期待。

你可以试试这个

context.words.Add(words);
result=context.SaveChanges();