Skip to content

Commit

Permalink
Improve perf for iterating over tracked entities
Browse files Browse the repository at this point in the history
Part of #8898
Fixes #14231

Investigation into DbSet.Local shows that:
* Iteration over tracked entities had a lot of LINQ code; fixed by un-LINQing
* Multiple composed iterators cause slow-down; fixed by giving LocalView it's only IStateManager method
* Filtering causes slow-down; fixed by splitting storage into multiple dictionaries. Makes lookup for entity instance slightly slower.
* Calculate Count lazily

This means that DbSet.Local iteration is now about 3x faster, although it depends a lot on what is being tracked. Getting an ObservableCollection and then iterating over that _once_ is slower than just iterating over the entries once. Iterating over the ObservableCollection multiple times is still faster than iterating over DbSet.Local multiple times. This seems like a reasonable trade-off. If the application does heavy iteration, then creating a new collection for that seems reasonable.

With regard to #8898, the layering we have still seems correct, so moving forward with the breaking changes there.
  • Loading branch information
ajcvickers committed Mar 5, 2019
1 parent c83944d commit f296d3f
Show file tree
Hide file tree
Showing 16 changed files with 756 additions and 210 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1725,12 +1725,9 @@ protected virtual IEnumerable<MigrationOperation> GetDataOperations()
{
if (_sourceStateManager != null)
{
foreach (var sourceEntry in _sourceStateManager.Entries.ToList())
foreach (var sourceEntry in _sourceStateManager.ToListForState(added: true))
{
if (sourceEntry.EntityState == EntityState.Added)
{
sourceEntry.SetEntityState(EntityState.Detached);
}
sourceEntry.SetEntityState(EntityState.Detached);
}
}

Expand Down
9 changes: 4 additions & 5 deletions src/EFCore/ChangeTracking/Internal/ChangeDetector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,11 @@ public virtual void DetectChanges(IStateManager stateManager)
{
_logger.DetectChangesStarting(stateManager.Context);

foreach (var entry in stateManager.Entries.Where(
e => e.EntityState != EntityState.Detached
&& e.EntityType.GetChangeTrackingStrategy() == ChangeTrackingStrategy.Snapshot).ToList())
foreach (var entry in stateManager.ToList()) // Might be too big, but usually _all_ entities are using Snapshot tracking

{
// State might change while detecting changes on other entries
if (entry.EntityState != EntityState.Detached)
if (entry.EntityType.GetChangeTrackingStrategy() == ChangeTrackingStrategy.Snapshot
&& entry.EntityState != EntityState.Detached)
{
LocalDetectChanges(entry);
}
Expand Down
Loading

0 comments on commit f296d3f

Please sign in to comment.