How do I clear tracked entities in entity framework
C#Entity FrameworkC# Problem Overview
I am running some correction code that runs over a big pile of entities, as it progress its speed decreases, that is because the number of tracked entities in the context increase with each iteration, It can take long so I am saving changes at the end of each iteration. Each iteration is independent and does not change the previosuly loaded entities.
I know I can turn off change tracking but I do not want to, because it is not a bulk insert code, but loading the entities and calculating a few things and if the numbers are not correct set the new numbers and update/delete/create some additional entities. I know I can create a new DbContext for each iteration and probably that would run faster than doing all in the same instance, but I am thinking that there might be a better way.
So the question is; Is there a way of clearing the entities previously loaded in the db context?
C# Solutions
Solution 1 - C#
You can add a method to your DbContext
or an extension method that uses the ChangeTracker to detach all the Added, Modified, and Deleted entities:
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;
}
Solution 2 - C#
EntityFramework Core 5.0 introduced a new method to clear any tracked changes.
_context.ChangeTracker.Clear();
Solution 3 - C#
1. Possibility: detach the entry
dbContext.Entry(entity).State = EntityState.Detached;
When you detach the entry the change tracker will stop tracking it (and should result in better performance)
See: http://msdn.microsoft.com/de-de/library/system.data.entitystate(v=vs.110).aspx
2. Possibility: work with your own Status
field + disconnected contexts
Maybe you want to control the status of your entity independently so you can use disconnected graphs. Add a property for the entity status and transform this status into the dbContext.Entry(entity).State
when performing operations (use a repository to do this)
public class Foo
{
public EntityStatus EntityStatus { get; set; }
}
public enum EntityStatus
{
Unmodified,
Modified,
Added
}
See following link for an example: https://www.safaribooksonline.com/library/view/programming-entity-framework/9781449331825/ch04s06.html
Solution 4 - C#
I'm running a windows service that updates values every minute and I have had the same problem. I tried running @DavidSherrets solution but after a few hours this got slow as well. My solution was to simply create a new context like this for every new run. Simple but it works.
_dbContext = new DbContext();
Solution 5 - C#
I just ran into this issue, and eventually stumbled upon a better solution for those using the typical .NET Core dependency injection. You can use a scoped DbContext for each operation. That will reset DbContext.ChangeTracker
so that SaveChangesAsync()
won't get bogged down checking entities from past iterations. Here is an example ASP.NET Core Controller method:
/// <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();
}
Given that ASP.NET Core projects typically use DbContextPool, this doesn't even create/destroy the DbContext objects. (In case you were interested, DbContextPool actually calls DbContext.ResetState()
and DbContext.Resurrect()
, but I wouldn't recommend calling those directly from your code, as they will probably change in future releases.)
https://github.com/aspnet/EntityFrameworkCore/blob/v2.2.1/src/EFCore/Internal/DbContextPool.cs#L157
Solution 6 - C#
From EF Core 3.0 there is an internal API that can reset the ChangeTracker. Do not use this in production code, I mention it as it may help someone in testing depending on the scenario.
((IResettableService)ChangeTracker).ResetState();
As the comment on the code says;
> This is an internal API that supports the Entity Framework Core > infrastructure and not subject to the same compatibility standards as > public APIs. It may be changed or removed without notice in any > release. You should only use it directly in your code with extreme > caution and knowing that doing so can result in application failures > when updating to a new Entity Framework Core release.