代码之家  ›  专栏  ›  技术社区  ›  FloatingKiwi

在EntityFramework中使用AsNoTracking时出现问题

  •  4
  • FloatingKiwi  · 技术社区  · 9 年前

    我正在尝试对一些实体进行深度克隆。中提到的方法 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中已存在具有相同键的对象。现有对象处于“已修改”状态。仅当对象处于已添加状态时,才能再次将其添加到ObjectStateManager。

    当我修改成绩时。将其命名为EntityState更改为modified,表示它正在被跟踪。

    我正在使用EF5 Db First。

    或者,我尝试通过分离坡度然后重新插入来克隆,这是可行的,但坡度宽度不会被复制。我一调用detach,坡度宽度计数就从2变为0。

    问题:

    1. 或者,对于简单的深度克隆,有没有其他方法可以推荐?

    谢谢

    ----其他信息----

    我有5个一对多的关系,最终我想从顶层一直克隆下去。但我将其简化为只查看最低级别。

    • 1级到多级宽度
    2 回复  |  直到 8 年前
        1
  •  2
  •   Reza Aghaei    9 年前

    使用ObjectContext

    使用 ObjectContext 您可以先附加父对象,然后将对象状态更改为added。同样,对于每个子对象,将对象状态更改为added。你甚至不需要 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
    
        2
  •  1
  •   bubi    9 年前

    没有模型很难理解确切的行为(在这种情况下,主键很重要)。
    此外,我没有使用EF 5,但EF 6应该非常相似。

    在你的应用程序中,你正在从数据库中读取一个具有相关实体(坡度宽度)的实体,你需要将其再次添加到数据库中。
    我假设您的模型是1 Grade n GradeWidths,并且您正在创建从原始Grade复制的GradeWitths的新实例(您的模型可以是n-m,在这种情况下,您可以在更多Grade上使用1 GradeWith,但我认为您不是)。

    在这种情况下,要添加新实体,需要重置所有ID、坡度ID和坡度宽度ID。 然后,您有一个“干净”克隆,可以将其添加到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);
    }