简体   繁体   中英

How do I clear tracked entities in entity framework

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?

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;
}

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

EntityFramework Core 5.0 introduced a new method to clear any tracked changes.

_context.ChangeTracker.Clear();

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

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

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

从.net5.0开始,你可以使用这个: https ://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.changetracking.changetracker.clear?view=efcore-5.0

dbContext.ChangeTracker.Clear();

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.

Well my opinion is that, in my experience, EF, or being any orm, does not work well under too much pressure or complex model.

If you don't want to track, really I would say why even do orm?

If speed is the main force, nothing beats stored procedures and good indexing.

And beyond, if your queries are always per id consider using a nosql or perhaps sql with just key and json. This would avoid the impedance problem between classes and tables.

For your case scenario, loading things in objects that way seems very slow to me. Really in your case, stored procedures are better because you avoid the transport of data thru the network, and sql is way faster and optimized to manage aggregation and things like that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM