我正在构建一个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;
}
我无法相信我有这么多问题。我只想存储这些实体,添加一些(我还没有达到改变它们的程度)并保存它们。
到目前为止,我已经想过:
所有这些都可能导致问题,但我不知道哪些以及如何解决这些问题。
我想我可以通过一次添加一个单词,每次都拉出程序,课程和单元来完成这项工作......但这意味着要多次往返DB。这不可能。
答案 0 :(得分:3)
经过一段时间回到这个问题,在这种情况下的问题是我需要检索我的关系中存在的实体。
解决方案既不附加(因为如果实体已经附加就会失败)也不会添加(因为它已经存在于数据库中)。
我应该做的是检索与我正在添加的实体相关的每个实体。
答案 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();