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

我正在运行一些修正代码,这些代码覆盖了一大堆实体,因为它的运行速度在下降,这是因为在上下文中被跟踪的实体的数量随着每次迭代的增加而增加。这可能会花费很长的时间,所以我在每次迭代结束时保存修改。每个迭代都是独立的,并且不更改以前加载的实体。

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

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

113097 次浏览

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

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

我正在运行一个窗口服务,每分钟更新值,我有同样的问题。我试着运行@DavidSherret 解决方案,但是几个小时后,它也变慢了。我的解决方案是为每个新的运行创建一个像这样的新上下文。很简单,但很管用。

_dbContext = new DbContext();

我只是碰到了这个问题,并最终偶然发现了一个更好的解决方案,为那些使用典型。NET 核心依赖注入。可以对每个操作使用限定作用域的 DbContext。这将重置 DbContext.ChangeTracker,这样 SaveChangesAsync()就不会在检查过去迭代的实体时陷入困境。下面是一个 ASP.NET 核心控制器方法的例子:

    /// <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

EF Core 3.0有一个 内部空气污染指数可以重置 ChangeTracker。不要在生产代码中使用它,我提到它是因为它可以帮助某人根据场景进行测试。

((IResettableService)ChangeTracker).ResetState();

正如对 密码的评论所说:

这是一个支持实体框架核心的内部 API 基础设施,不受相同的兼容性标准 公共应用程式介面的资料,可在没有通知的情况下更改或移除 你应该只在你的代码中使用它 警告并知道这样做可能导致应用程序失败 当更新到新的实体框架核心版本时。

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

_context.ChangeTracker.Clear();

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