Skip to content

Commit

Permalink
Perf: Cache SkipDetectChanges on model as a field
Browse files Browse the repository at this point in the history
Resolves #22262
  • Loading branch information
smitpatel committed Aug 27, 2020
1 parent 2b6334e commit 2e69c34
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/EFCore/ChangeTracking/ChangeTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ public virtual bool HasChanges()
/// </summary>
public virtual void DetectChanges()
{
if ((string)_model[CoreAnnotationNames.SkipDetectChangesAnnotation] != "true")
if (!((Model)_model).SkipDetectChanges)
{
ChangeDetector.DetectChanges(StateManager);
}
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/ChangeTracking/CollectionEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private void LocalDetectChanges()
var context = InternalEntry.StateManager.Context;

var changeDetector = context.ChangeTracker.AutoDetectChangesEnabled
&& (string)context.Model[CoreAnnotationNames.SkipDetectChangesAnnotation] != "true"
&& !((Model)context.Model).SkipDetectChanges
? context.GetDependencies().ChangeDetector
: null;

Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/ChangeTracking/EntityEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public virtual EntityState State
/// </summary>
public virtual void DetectChanges()
{
if ((string)Context.Model[CoreAnnotationNames.SkipDetectChangesAnnotation] != "true")
if (!((Model)Context.Model).SkipDetectChanges)
{
Context.GetDependencies().ChangeDetector.DetectChanges(InternalEntry);
}
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 @@ -993,7 +993,7 @@ public virtual void CascadeDelete(InternalEntityEntry entry, bool force, IEnumer
if (!_changeDetectorInitialized)
{
_changeDetector = Context.ChangeTracker.AutoDetectChangesEnabled
&& (string)Context.Model[CoreAnnotationNames.SkipDetectChangesAnnotation] != "true"
&& !((Model)Context.Model).SkipDetectChanges
? Context.GetDependencies().ChangeDetector
: null;
_changeDetectorInitialized = true;
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/ChangeTracking/ReferenceEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private void LocalDetectChanges()
{
var context = InternalEntry.StateManager.Context;
if (context.ChangeTracker.AutoDetectChangesEnabled
&& (string)context.Model[CoreAnnotationNames.SkipDetectChangesAnnotation] != "true")
&& !((Model)context.Model).SkipDetectChanges)
{
context.GetDependencies().ChangeDetector.DetectChanges(target);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ public virtual void ProcessModelFinalizing(
}
}

modelBuilder.HasAnnotation(CoreAnnotationNames.SkipDetectChangesAnnotation, "true");
if (modelBuilder.Metadata is Model model)
{
model.SetSkipDetectChanges(true, ConfigurationSource.Convention);
}
}
}
}
9 changes: 0 additions & 9 deletions src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,6 @@ public static class CoreAnnotationNames
/// </summary>
public const string AmbiguousField = "BackingFieldConvention:AmbiguousField";

/// <summary>
/// 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.
/// </summary>
public const string SkipDetectChangesAnnotation = "ChangeDetector.SkipDetectChanges";

/// <summary>
/// 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
Expand Down Expand Up @@ -315,7 +307,6 @@ public static class CoreAnnotationNames
AmbiguousNavigations,
DuplicateServiceProperties,
AmbiguousField,
SkipDetectChangesAnnotation,
SkipChangeTrackingStrategyValidationAnnotation
};
}
Expand Down
38 changes: 38 additions & 0 deletions src/EFCore/Metadata/Internal/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ private readonly Dictionary<string, ConfigurationSource> _ignoredTypeNames
private readonly Dictionary<Type, ConfigurationSource> _sharedTypes =
new Dictionary<Type, ConfigurationSource> { { DefaultPropertyBagType, ConfigurationSource.Convention } };

private bool? _skipDetectChanges;
private ConfigurationSource? _skipDetectChangesConfigurationSource;

/// <summary>
/// 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
Expand Down Expand Up @@ -989,6 +992,41 @@ private IModel MakeReadonly()
public virtual PropertyInfo FindIndexerPropertyInfo([NotNull] Type type)
=> _indexerPropertyInfoMap.GetOrAdd(type, type.FindIndexerProperty());

/// <summary>
/// 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.
/// </summary>
public virtual bool SkipDetectChanges
{
get => _skipDetectChanges ?? false;
set => SetSkipDetectChanges(value, ConfigurationSource.Explicit);
}

/// <summary>
/// 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.
/// </summary>
public virtual bool? SetSkipDetectChanges(bool? skipDetectChanges, ConfigurationSource configurationSource)
{
if (skipDetectChanges == null)
{
_skipDetectChanges = null;
_skipDetectChangesConfigurationSource = null;

return skipDetectChanges;
}

_skipDetectChangesConfigurationSource = configurationSource.Max(_skipDetectChangesConfigurationSource);

_skipDetectChanges = skipDetectChanges;

return skipDetectChanges;
}

/// <summary>
/// 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
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Update/Internal/UpdateAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public virtual IEnumerable<IUpdateEntry> Entries
/// </summary>
public virtual void DetectChanges()
{
if ((string)_stateManager.Model[CoreAnnotationNames.SkipDetectChangesAnnotation] != "true")
if (!((Model)_stateManager.Model).SkipDetectChanges)
{
_changeDetector.DetectChanges(_stateManager);
}
Expand Down

0 comments on commit 2e69c34

Please sign in to comment.