我试图对某些实体进行深度克隆。 this article中提到的方法看起来不错,但我遇到了错误。它建议使用AsNoTracking()来检索实体,然后将其重新插入到上下文中,它将导致插入,因为它看起来像一个新对象。
这是我的代码:
Using ctxt As New ProductionDataEntities
Dim grade = ctxt.Grades.Include(Function(g) g.GradeWidths).AsNoTracking.First
ctxt.Grades.AddObject(grade)
ctxt.SaveChanges()
End Using
但是当我跑步时,我得到了:
ObjectStateManager中已存在具有相同键的对象。现有对象处于Modified状态。如果对象处于添加状态,则只能再次添加到ObjectStateManager。
当我修改grade.Name时,它的EntityState更改为已修改,暗示它正在被跟踪。
我使用的是EF5 Db-First。
或者,我已经通过分离成绩然后重新插入来尝试克隆,但这可以正常工作,但GradeWidths不会被复制。一旦我调用detach,等级宽度计数从2变为0。
问题:
感谢。
----附加信息----
我有5个1对多的关系,最终我想要从顶层一直克隆下来。但我简化它只是看最低级别。
答案 0 :(得分:1)
使用ObjectContext
使用ObjectContext
,您可以先附加父对象,然后将对象状态更改为已添加。同样对于每个子对象,将对象状态更改为已添加。你甚至不需要AsNoTracking()
。
例如,对于Category(1)↔(N)Product
关系,我使用了此代码:
Using db As New SampleSystemEntities
Dim c = db.Categories.Include(Function(x) x.Products).First
db.Attach(c)
db.ObjectStateManager.ChangeObjectState(c, EntityState.Added)
For Each p As Product In c.Products
db.ObjectStateManager.ChangeObjectState(p, EntityState.Added)
Next
db.SaveChanges()
End Using
使用DbContext
如果你使用DbContext
,那么使用你的代码就可以正常工作:
Using db As New SampleSystemEntities
Dim c = db.Categories.Include(Function(x) x.Products).AsNoTracking().First
db.Categories.Add(c)
db.SaveChanges()
End Using
答案 1 :(得分:0)
没有模型很难理解确切的行为(在这种情况下,主要键很重要) 另外,我没有使用EF 5,但EF 6应该非常相似。
在您的应用中,您正在从数据库中读取具有相关实体(GradeWidths)的实体,您需要将其再次添加到数据库中。 我认为你的模型是1级n GradeWidths并且你正在创建从原始等级复制的GradeWidth的新实例(你的模型可能是nm,在这种情况下你可以在更多等级上使用1 GradeWidth,但我想你不是)。 / p>
在这种情况下,要添加新实体,您需要重置所有ID,Grade和GradeWidths。 然后,你有一个“干净”的克隆,你可以将它添加到DbSet(添加,而不是附加)。
using (var context = new MyContext(connection))
{
context.Grades.Add(new Grade()
{
GradeWidths = new List<GradeWidth>(new[]
{
new GradeWidth() {Width = 10},
new GradeWidth() {Width = 20},
new GradeWidth() {Width = 30}
})
});
context.SaveChanges();
}
using (var context = new MyContext(connection))
{
Grade grade = context.Grades.Include(g => g.GradeWidths).AsNoTracking().First();
// We need to reset all the ids
grade.Id = 0;
foreach (GradeWidth gradeWidth in grade.GradeWidths)
gradeWidth.Id = 0;
context.Grades.Add(grade);
context.SaveChanges();
}
using (var context = new MyContext(connection))
{
Debug.Assert(context.Grades.Count() == 2);
Debug.Assert(context.GradeWidths.Count() == 6);
}