我正在运行一些运行在大量实体上的修正代码,因为它的速度会降低,这是因为上下文中跟踪实体的数量随着每次迭代而增加,可能需要很长时间才能保存更改在每次迭代结束时。每次迭代都是独立的,不会改变先前加载的实体。
我知道我可以关闭更改跟踪,但我不想,因为它不是批量插入代码,而是加载实体并计算一些东西,如果数字不正确,请设置新数字并更新/删除/创建一些其他实体。我知道我可以为每次迭代创建一个新的DbContext,并且可能比在同一个实例中运行得更快,但我认为可能有更好的方法。
所以问题是;有没有办法清除以前在db上下文中加载的实体?
答案 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)
从.net5.0开始,可以使用这个: https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.changetracking.changetracker.clear?view=efcore-5.0
dbContext.ChangeTracker.Clear();
答案 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();