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

时间:2014-12-11 12:35:29

标签: c# entity-framework

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

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

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

8 个答案:

答案 0 :(得分:68)

您可以向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;
}

答案 1 :(得分:12)

<强> 1。可能性:分离条目

dbContext.Entry(entity).State = EntityState.Detached;

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

请参阅: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
}

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

答案 2 :(得分:8)

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

_dbContext = new DbContext();

答案 3 :(得分:2)

我只是遇到了这个问题,最终偶然发现了一个使用典型的.NET Core依赖项注入的更好的解决方案。您可以为每个操作使用范围限定的DbContext。这将重置DbContext.ChangeTracker,以使SaveChangesAsync()不会因过去的迭代而陷入检查实体的困境。这是一个示例ASP.NET Core Controller方法:

    /// <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();
    }

鉴于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

答案 4 :(得分:2)

答案 5 :(得分:1)

我的观点是,以我的经验来看,EF或作为任何Orm在压力太大或模型复杂的情况下都无法很好地工作。

如果您不想跟踪,我真的会说为什么甚至要执行orm?

如果速度是主要动力,那么存储过程和良好的索引将无可匹敌。

此外,如果您的查询始终按ID进行,请考虑使用nosql或仅包含key和json的sql。这样可以避免类和表之间的阻抗问题。

对于您的情况,对我来说,以这种方式加载东西似乎很慢。确实,在您的情况下,存储过程更好,因为可以避免通过网络传输数据,而sql则速度更快,并且经过优化,可以管理聚合以及类似的事情。

答案 6 :(得分:1)

从EF Core 3.0开始,内部API 可以重置ChangeTracker。不要在生产代码中使用它,我会提到它,因为它可能会根据情况帮助某人进行测试。

using Microsoft.EntityFrameworkCore.Internal;

_context.GetDependencies().StateManager.ResetState();

如对代码的注释所述;

这是支持Entity Framework Core的内部API 基础架构,并且不受与相同的兼容性标准的约束 公共API。它可能会在不另行通知的情况下进行更改或删除 发布。您只应在极端情况下直接在代码中使用它 注意并知道这样做可能会导致应用程序失败 更新到新的Entity Framework Core版本时。

答案 7 :(得分:0)

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

_context.ChangeTracker.Clear();

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