【问题标题】:How to clean-up an Entity Framework object context?如何清理实体框架对象上下文?
【发布时间】:2010-03-17 21:09:13
【问题描述】:

我正在向一个对象上下文添加几个实体。

try
{
    forach (var document in documents)
    {
        this.Validate(document); // May throw a ValidationException.

        this.objectContext.AddToDocuments(document);
    }

    this.objectContext.SaveChanges();
}
catch
{
    // How to clean-up the object context here?

    throw;
}

如果一些文档通过验证而一个失败,所有通过验证的文档仍然添加到对象上下文中。我必须清理对象上下文,因为它可能会被重用,并且可能会发生以下情况。

var documentA = new Document { Id = 1, Data = "ValidData" };
var documentB = new Document { Id = 2, Data = "InvalidData" };
var documentC = new Document { Id = 3, Data = "ValidData" };

try
{
    // Adding document B will cause a ValidationException but only
    // after document A is added to the object context.
    this.DocumentStore.AddDocuments(new[] { documentA, documentB, documentC });
}
catch (ValidationException)
{
}

// Try again without the invalid document B. This causes an exception because
// of a duplicate primary key - document A with id 1 is added a second time.
this.DocumentStore.AddDocuments(new[] { documentA, documentC });

这将再次将文档 A 添加到对象上下文中,因此 SaveChanges() 将由于主键重复而引发异常。

因此,如果出现验证错误,我必须删除所有已添加的文档。我当然可以先执行验证,然后仅在成功验证后添加所有文档,但遗憾的是,这并不能解决整个问题 - 如果 SaveChanges() 失败,所有文档仍会被添加但未保存。

我试图分离所有返回的对象

 this.objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added)

但我收到一个异常,指出该对象未附加。那么如何删除所有添加但未保存的对象呢?

【问题讨论】:

  • 我建议不要重用对象上下文。在某些情况下它是有意义的,但它们非常罕见。请记住,使用 ObjectContext 的时间越长,它就会变得越臃肿,如果它进入不一致的状态(即部分完成的事情),您也可能会得到一些不一致的副作用。

标签: c# entity-framework


【解决方案1】:

Daniel 的回答对我有用,但是 EntityFramework API 在版本 6+ 中有所不同。这是我添加到自定义存储库容器中的一种方法,该方法将从 DbContext 的 ChangeTracker 中分离所有实体:

    /// <summary>
    /// Detaches all of the DbEntityEntry objects that have been added to the ChangeTracker.
    /// </summary>
    public void DetachAll() {

        foreach (DbEntityEntry dbEntityEntry in this.Context.ChangeTracker.Entries().ToArray()) {

            if (dbEntityEntry.Entity != null) {
                dbEntityEntry.State = EntityState.Detached;
            }
        }
    }

【讨论】:

    【解决方案2】:

    这只是一个微不足道的错误,但我将把问题留在这里 - 也许它可以帮助其他人。

    我有以下

    var objectStateEntries = this.objectContext
                                 .ObjectStateManager
                                 .GetObjectStateEntries(EntityState.Added);
    
    foreach (var objectStateEntry in objectStateEntries)
    {
        this.objectContext.Detach(objectStateEntry);
    }
    

    虽然我想要以下内容

    foreach (var objectStateEntry in objectStateEntries)
    {
        this.objectContext.Detach(objectStateEntry.Entity);
    }
    

    而且看不到。

    【讨论】:

    • 顺便说一句,可以使用按位标志来执行 var objectStateEntries = this._context.ObjectStateManager .GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged);分离所有附加的对象
    • 请注意,在某些情况下,objectStateEntry.Entity 将为空(objectStateEntity 描述与无实体的关系),因此您可能应该在其中放置一个空检查。
    【解决方案3】:

    Since EF 5.0 使用 ChangeTracker.Clear() 清除所有跟踪实体的 DbContext。

    【讨论】:

      【解决方案4】:

      聚会有点晚了,但您考虑过工作单元模式吗?

      简而言之,它允许您拥有一个事务,然后您可以回滚(处置)或提交(保存更改)

      注意:这是将更改分组到一个操作中的事务,而不是 sql BEGIN TRAN 中的事务。 SaveChanges() 方法仍会为您处理这一切。

      类似:

      Public Class UnitOfWork
          Implements IUnitOfWork
          Implements IDisposable
          Private ReadOnly Property ObjectContext As Interfaces.IObjectContext
      
          Public Sub New(ByVal objectContext As Interfaces.IObjectContext)
              _objectContext = objectContext
          End Sub
      
          Public Sub Dispose() Implements IDisposable.Dispose
              If _objectContext IsNot Nothing Then
                  _objectContext.Dispose()
              End If
              GC.SuppressFinalize(Me)
          End Sub
      
          Public Sub Commit() Implements IUnitOfWork.Commit
              _objectContext.SaveChanges()
          End Sub
      End Class
      

      每次您创建一个工作单元时,它都会包含一个 objectcontext - 我们使用 Unity 来生成这些,以便在旧的已被处置时创建一个新的

      【讨论】:

      • 使用工作单元仍然不会将对象从上下文中分离出来。它也没有回答这个问题。
      • @AgentShark 完全取决于您如何实现Rollback 方法。此外,在已接受的答案中复制正确的代码是没有意义的,它明确显示了如何分离对象
      • 我重读了这个问题。是的,通过工作单元强制更新会很有用。但是在 EF 中,添加具有相关实体的对象时必须小心。存在具有不同行为的不同方式将对象添加/保存到上下文中。
      【解决方案5】:
      var entries = ((DbContext)ef).ChangeTracker.Entries();
      foreach (var entry in entries)
      {
          entry.State = System.Data.EntityState.Detached;
      }
      

      【讨论】:

        【解决方案6】:

        对于那些使用 Entity Framework Core 1.0 RC2+ 的人,我更新了 Garrys 的答案。

        参考

        using Microsoft.EntityFrameworkCore;
        using Microsoft.EntityFrameworkCore.ChangeTracking;
        

        更新代码

                /// <summary>
                /// Detaches all of the EntityEntry objects that have been added to the ChangeTracker.
                /// </summary>
                public void DetachAll()
                {
                    foreach (EntityEntry entityEntry in this.Context.ChangeTracker.Entries().ToArray())
                    {
                        if (entityEntry.Entity != null)
                        {
                            entityEntry.State = EntityState.Detached;
                        }
                    }
                }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-11-11
          • 2014-03-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多