Reputation: 6029
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 previously 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 my question: is there a way of clearing the entities previously loaded in the DbContext
?
Upvotes: 179
Views: 166757
Reputation: 4200
With Microsoft.EntityFrameworkCore
v.8.0 and above you can clear tracked entities for the specific table. Very often I tend to use something like bellow:
When I do need to update the table in the following way
await _db.Set<Contacts>()
.Where(x => x.FirstName == "John")
.ExecuteUpdateAsync(x => x
.SetProperty(p => p.Gender, "Male")
);
The changes made via ExecuteUpdate
or by pure SqlCommand
do not reflect the tracked entities. So in this case it is recommended to clear those entities stored (cached) in memory just in case because they could contain outdated data.
According to microsoft docs:
ExecuteUpdate
andExecuteDelete
are a way to save data to the database without using EF's traditional change tracking andSaveChanges()
method. Crucially, when ExecuteUpdate is invoked and all Blogs are updated in the database, EF's change tracker is not updated, and the tracked .NET instance still has its original rating value, from the point at which it was queried.
The way you can do that is by accessing the local tracked store. There are few options available depending on your need (Local, ReloadAsync, ChangeTracker.Clear).
// detach all the contacts from the cache
foreach (var entity in _db.Set<Contacts>().Local)
{
var entry = _db.Entry(entity);
if (entry.State != EntityState.Added)
entry.State = EntityState.Detached;
}
// or alternatively reload those entities
foreach (var entity in _db.Set<Contacts>().Local)
{
await _db.Entry(entity).ReloadAsync();
}
// or of course you can clear all the existing tracked entities
_db.ChangeTracker.Clear();
Upvotes: 0
Reputation: 106840
Nowadays, you can just call context.ChangeTracker.Clear();
if you're using EF Core 5.0+
Otherwise, you can add a method to your DbContext
or an extension method that uses the ChangeTracker to detach all the Added, Modified, Deleted, and Changed entities:
public void DetachAllEntities()
{
var undetachedEntriesCopy = this.ChangeTracker.Entries()
.Where(e => e.State != EntityState.Detached)
.ToList();
foreach (var entry in undetachedEntriesCopy)
entry.State = EntityState.Detached;
}
Upvotes: 204
Reputation: 8953
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.
Upvotes: 2
Reputation: 3026
EntityFramework Core 5.0 introduced a new method to clear any tracked changes.
_context.ChangeTracker.Clear();
Upvotes: 160
Reputation: 729
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
Upvotes: 10
Reputation: 70184
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();
Upvotes: 22
Reputation: 2532
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
Upvotes: 38