Skip to content

Commit

Permalink
Perf: Cache candidate properties for propagation and generation
Browse files Browse the repository at this point in the history
Resolves #22263

Also improves perf for Add
  • Loading branch information
smitpatel committed Aug 27, 2020
1 parent a57d2e5 commit 58c065c
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 17 deletions.
3 changes: 2 additions & 1 deletion src/EFCore/ChangeTracking/Internal/EntityReferenceMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public virtual void Update(
EntityState state,
EntityState? oldState)
{
var mapKey = entry.Entity ?? entry;
var entityType = entry.EntityType;
if (_hasSubMap
&& entityType.HasDefiningNavigation())
Expand All @@ -70,6 +69,8 @@ public virtual void Update(
}
else
{
var mapKey = entry.Entity ?? entry;

if (oldState.HasValue)
{
Remove(mapKey, entityType, oldState.Value);
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/ChangeTracking/Internal/StateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,9 @@ private void UpdateReferenceMaps(
EntityState? oldState)
{
var entityType = entry.EntityType;
var mapKey = entry.Entity ?? entry;
if (entityType.HasDefiningNavigation())
{
var mapKey = entry.Entity ?? entry;
foreach (var otherType in _model.GetEntityTypes(entityType.Name)
.Where(et => et != entityType && TryGetEntry(mapKey, et) != null))
{
Expand Down
63 changes: 48 additions & 15 deletions src/EFCore/ChangeTracking/Internal/ValueGenerationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public class ValueGenerationManager : IValueGenerationManager
private readonly IKeyPropagator _keyPropagator;
private readonly IDiagnosticsLogger<DbLoggerCategory.ChangeTracking> _logger;
private readonly ILoggingOptions _loggingOptions;
private readonly Dictionary<IEntityType, List<IProperty>> _entityTypePropagatingPropertiesMap
= new Dictionary<IEntityType, List<IProperty>>();
private readonly Dictionary<(IEntityType, bool), List<IProperty>> _entityTypeGeneratingPropertiesMap
= new Dictionary<(IEntityType, bool), List<IProperty>>();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -61,8 +65,13 @@ public ValueGenerationManager(
public virtual InternalEntityEntry Propagate(InternalEntityEntry entry)
{
InternalEntityEntry chosenPrincipal = null;
foreach (var property in FindPropagatingProperties(entry))
foreach (var property in FindCandidatePropagatingProperties(entry))
{
if (!entry.HasDefaultValue(property))
{
continue;
}

var principalEntry = _keyPropagator.PropagateValue(entry, property);
if (chosenPrincipal == null)
{
Expand All @@ -83,8 +92,13 @@ public virtual void Generate(InternalEntityEntry entry, bool includePKs = true)
{
var entityEntry = new EntityEntry(entry);

foreach (var property in FindGeneratingProperties(entry, includePKs))
foreach (var property in FindCandidateGeneratingProperties(entry, includePKs))
{
if (!entry.HasDefaultValue(property))
{
continue;
}

var valueGenerator = GetValueGenerator(entry, property);

var generatedValue = valueGenerator.Next(entityEntry);
Expand Down Expand Up @@ -125,8 +139,13 @@ public virtual async Task GenerateAsync(
{
var entityEntry = new EntityEntry(entry);

foreach (var property in FindGeneratingProperties(entry, includePKs))
foreach (var property in FindCandidateGeneratingProperties(entry, includePKs))
{
if (!entry.HasDefaultValue(property))
{
continue;
}

var valueGenerator = GetValueGenerator(entry, property);
var generatedValue = await valueGenerator.NextAsync(entityEntry, cancellationToken)
.ConfigureAwait(false);
Expand All @@ -142,30 +161,44 @@ public virtual async Task GenerateAsync(
}
}

private static IEnumerable<IProperty> FindPropagatingProperties(InternalEntityEntry entry)
private IReadOnlyList<IProperty> FindCandidatePropagatingProperties(InternalEntityEntry entry)
{
foreach (var property in ((EntityType)entry.EntityType).GetProperties())
if (!_entityTypePropagatingPropertiesMap.TryGetValue(entry.EntityType, out var candidateProperties))
{
if (property.IsForeignKey()
&& entry.HasDefaultValue(property))
candidateProperties = new List<IProperty>();
foreach (var property in ((EntityType)entry.EntityType).GetProperties())
{
yield return property;
if (property.IsForeignKey())
{
candidateProperties.Add(property);
}
}

_entityTypePropagatingPropertiesMap[entry.EntityType] = candidateProperties;
}

return candidateProperties;
}

private static IEnumerable<IProperty> FindGeneratingProperties(InternalEntityEntry entry, bool includePKs = true)
private IReadOnlyList<IProperty> FindCandidateGeneratingProperties(InternalEntityEntry entry, bool includePKs = true)
{
foreach (var property in ((EntityType)entry.EntityType).GetProperties())
if (!_entityTypeGeneratingPropertiesMap.TryGetValue((entry.EntityType, includePKs), out var candidateProperties))
{
if (property.RequiresValueGenerator()
&& entry.HasDefaultValue(property)
&& (includePKs
|| !property.IsPrimaryKey()))
candidateProperties = new List<IProperty>();
foreach (var property in ((EntityType)entry.EntityType).GetProperties())
{
yield return property;
if (property.RequiresValueGenerator()
&& (includePKs
|| !property.IsPrimaryKey()))
{
candidateProperties.Add(property);
}
}

_entityTypeGeneratingPropertiesMap[(entry.EntityType, includePKs)] = candidateProperties;
}

return candidateProperties;
}

private ValueGenerator GetValueGenerator(InternalEntityEntry entry, IProperty property)
Expand Down

0 comments on commit 58c065c

Please sign in to comment.