使用AsNoTracking和EntityFramework发行问题

时间:2016-09-05 14:23:03

标签: vb.net entity-framework entity-framework-5

我试图对某些实体进行深度克隆。 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。

问题:

  1. 任何想法为什么AsNoTracking不起作用以及我可以做些什么来解决这个问题?
  2. 另外,有人可以推荐另一种简单深度克隆的方法吗?
  3. 感谢。

    ----附加信息----

    我有5个1对多的关系,最终我想要从顶层一直克隆下来。但我简化它只是看最低级别。

    • 1年级到多个GradeWidth

2 个答案:

答案 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);
}