如何清除实体框架中的跟踪实体

haz*_*nli 52 c# entity-framework

我正在运行一些运行在一大堆实体上的校正码,因为它的速度会降低,这是因为上下文中跟踪实体的数量会随着每次迭代而增加,这可能需要很长时间,所以我最后会保存更改每次迭代.每次迭代都是独立的,不会改变先前加载的实体.

我知道我可以关闭更改跟踪,但我不想,因为它不是批量插入代码,而是加载实体并计算一些东西,如果数字不正确,请设置新数字并更新/删除/创建一些额外的实体.我知道我可以为每次迭代创建一个新的DbContext,并且可能比在同一个实例中执行所有操作更快,但我认为可能有更好的方法.

所以问题是; 有没有办法清除先前在db上下文中加载的实体?

Dav*_*ret 84

您可以向您DbContext或扩展方法添加一个方法,该方法使用ChangeTracker分离所有已添加,已修改和已删除的实体:

public void DetachAllEntities()
{
    var changedEntriesCopy = this.ChangeTracker.Entries()
        .Where(e => e.State == EntityState.Added ||
                    e.State == EntityState.Modified ||
                    e.State == EntityState.Deleted)
        .ToList();

    foreach (var entry in changedEntriesCopy)
        entry.State = EntityState.Detached;
}
Run Code Online (Sandbox Code Playgroud)

  • 在我的单元测试中,条目状态是"未修改",可能是因为我使用了在测试方法结束时回滚的事务.这意味着我必须将跟踪的条目状态设置为"已分离"而不检查当前状态,以便我的测试一次正确运行.我在回滚交易后立即调用上面的代码,但我得到了它,回滚肯定意味着未修改状态. (6认同)
  • 你不应该也使用 e.State == EntityState.Unchanged 吗?虽然实体没有改变,但它仍然在上下文中被跟踪,并且是在 DetectChanges 期间考虑的实体集的一部分。例如,您添加新实体(它处于已添加状态),调用 SaveChanges 并且添加的实体现在处于 Unchanged 状态(它违反 UnitOfWork 模式,但操作员询问:*我在每次迭代结束时保存更改*)。 (4认同)
  • 确保在“位置”之后调用“ ToList”。否则,将引发System.InvalidOperationException:'集合已被修改;枚举操作可能无法执行。” (3认同)
  • (而且 `var entity` 应该真的是 `var entry`,因为它是条目而不是实际的实体) (2认同)
  • @DavidSherret 认为可能是这样!我发现了这一点,因为在我的一个测试应用程序中,循环浏览 1000 个项目并标记为 Detached 用现有代码大约需要 6000 毫秒。新的大约 15ms :) (2认同)

j-p*_*tty 33

EntityFramework Core 5.0 引入了一种新方法来清除任何跟踪的更改。

_context.ChangeTracker.Clear();
Run Code Online (Sandbox Code Playgroud)

https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.changetracking.changetracker.clear?view=efcore-5.0

  • 这应该是 2022 年公认的答案。 (11认同)
  • 值得指出的是,这也是性能方面更好的答案。根据文档:“这种方法应该始终优于分离每个跟踪的实体。分离实体是一个缓慢的过程,可能会产生副作用。这种方法在从上下文中清除所有跟踪的实体方面要有效得多。” (6认同)

roa*_*242 17

1.可能性:分离条目

dbContext.Entry(entity).State = EntityState.Detached;
Run Code Online (Sandbox Code Playgroud)

当您分离条目时,更改跟踪器将停止跟踪它(并且应该导致更好的性能)

请参阅:http://msdn.microsoft.com/de-de/library/system.data.entitystate(v=vs.110).aspx

2.可能性:使用您自己的Status字段+断开连接的上下文

您可能希望独立控制实体的状态,以便可以使用断开连接的图形.为实体状态添加属性,并将此状态转换为dbContext.Entry(entity).State执行操作时的状态(使用存储库执行此操作)

public class Foo
{
    public EntityStatus EntityStatus { get; set; }
}

public enum EntityStatus
{
    Unmodified,
    Modified,
    Added
}
Run Code Online (Sandbox Code Playgroud)

请参阅以下链接以获取示例:https://www.safaribooksonline.com/library/view/programming-entity-framework/9781449331825/ch04s06.html


Ogg*_*las 12

我正在运行一个每分钟更新值的Windows服务,我遇到了同样的问题.我试过运行@DavidSherrets解决方案,但几个小时后这也变慢了.我的解决方案是为每次新的运行创建一个像这样的新上下文.简单但它的工作原理.

_dbContext = new DbContext();

  • 这不是您目标的“简单但可行”的解决方案。这是唯一正确的方法。上下文应尽可能少,每1个事务1个上下文是最佳选择。 (5认同)
  • 同意@pwrigshihanomoronimo,上下文遵循UnitOfWork 设计模式。正如 Martin Fowler 所定义: > 维护受业务事务影响的对象列表,并且 > 协调更改的写入和并发性的解决 > 问题。 (2认同)
  • 不确定这是否适用于依赖注入 (2认同)

Mat*_*att 6

我刚刚遇到了这个问题,最终偶然发现了一个更好的解决方案,适用于那些使用典型的 .NET Core 依赖注入的人。您可以为每个操作使用一个作用域 DbContext。这将重置,DbContext.ChangeTracker以便SaveChangesAsync()不会因为过去的迭代检查实体而陷入困境。这是一个示例 ASP.NET Core 控制器方法:

    /// <summary>
    /// An endpoint that processes a batch of records.
    /// </summary>
    /// <param name="provider">The service provider to create scoped DbContexts.
    /// This is injected by DI per the FromServices attribute.</param>
    /// <param name="records">The batch of records.</param>
    public async Task<IActionResult> PostRecords(
        [FromServices] IServiceProvider provider,
        Record[] records)
    {
        // The service scope factory is used to create a scope per iteration
        var serviceScopeFactory =
            provider.GetRequiredService<IServiceScopeFactory>();

        foreach (var record in records)
        {
            // At the end of the using block, scope.Dispose() will be called,
            // release the DbContext so it can be disposed/reset
            using (var scope = serviceScopeFactory.CreateScope())
            {
                var context = scope.ServiceProvider.GetService<MainDbContext>();

                // Query and modify database records as needed

                await context.SaveChangesAsync();
            }
        }

        return Ok();
    }
Run Code Online (Sandbox Code Playgroud)

鉴于 ASP.NET Core 项目通常使用 DbContextPool,这甚至不会创建/销毁 DbContext 对象。(如果您有兴趣,DbContextPool 实际上会调用DbContext.ResetState()DbContext.Resurrect(),但我不建议直接从您的代码中调用它们,因为它们可能会在未来版本中发生变化。) https://github.com/aspnet/EntityFrameworkCore/blob/v2 .2.1/src/EFCore/Internal/DbContextPool.cs#L157