【问题标题】:Issue using AsNoTracking with EntityFramework将 AsNoTracking 与 EntityFramework 一起使用的问题
【发布时间】:2016-09-17 23:29:02
【问题描述】:

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

当我修改 Grade.Name 时,它​​的 EntityState 更改为 modified 暗示它正在被跟踪。

我正在使用 EF5 Db-First。

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

问题:

  1. 知道为什么 AsNoTracking 不起作用,我可以做些什么来解决这个问题?
  2. 另外,还有其他人可以推荐用于简单深度克隆的方法吗?

谢谢。

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

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

  • 1 级到多个 GradeWidths

【问题讨论】:

  • 您想将实体附加到上下文,而不是添加它。无论如何,添加只会在表中创建一个新行。
  • @DavidG - 我正在尝试创建一个新行(即复制它)。此外,尽管我调用了 AsNoTracking,但我无法调用 attach,因为实体已经附加了
  • 啊,我应该读一下!那么你可能只需要对主键值进行闪电战。
  • 仍然打开。有人有什么想法吗?
  • 您是否尝试在添加主键之前将其设为空?或者如果是 int 则设为 0。

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


【解决方案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

【讨论】:

  • 谢谢礼萨。这是一个很好的选择,对我有用。我仍然对 AsNoTracking 无法在我的电脑上运行的原因感兴趣,因此我将把这个问题留待几天。
  • 没问题,如果我还有什么要分享的,我也会分享更多:)
  • 如果您使用DbContext,那么AsNoTracking 将按预期工作。你有什么要求让你使用AsNoTracking
  • 我对 Entity Framework 还是很陌生,所以我一直在阅读 ObjectContext 与 DbContext,但对于我的生活,我无法弄清楚如何获得 DbContext。尽管教程中提到了,该工具似乎总是生成 ObjectContext。知道有什么问题吗? VS2010? .net4.0?
  • 不客气。我也可以将 EF 5.0 与 VS2010 一起使用。我首先安装了 Entity Framework NuGet 包。然后添加EF 5.x DbContext Generator for VB.NET。创建我的 edmx 后,我删除了生成的代码并使用 Add Code Generation Item... 为 EF 5 添加了 t4 模板并使用 AsNoTracking 一切正常。
【解决方案2】:

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

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

在这种情况下,要添加新实体,您需要重置所有 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);
}

【讨论】:

  • 我收到一个错误The property 'GradeID' is part of the object's key information and cannot be modified. 只有当我分离对象但我丢失了 GradeWidths 集合时才会消失。
  • 在我的情况下,它工作正常并且对象已分离(在发送给您之前我运行代码)。 EF 5 中的 asnotracking 可能存在错误。无论如何,如果您分离主要对象,您还必须分离您需要添加它们的详细信息。
猜你喜欢
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 2016-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-12
相关资源
最近更新 更多