From 2e69c34c951ed05775d5ef2bdc767871e1dcb644 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Thu, 27 Aug 2020 15:31:29 -0700 Subject: [PATCH] Perf: Cache SkipDetectChanges on model as a field Resolves #22262 --- src/EFCore/ChangeTracking/ChangeTracker.cs | 2 +- src/EFCore/ChangeTracking/CollectionEntry.cs | 2 +- src/EFCore/ChangeTracking/EntityEntry.cs | 2 +- .../ChangeTracking/Internal/StateManager.cs | 2 +- src/EFCore/ChangeTracking/ReferenceEntry.cs | 2 +- .../ChangeTrackingStrategyConvention.cs | 5 ++- .../Metadata/Internal/CoreAnnotationNames.cs | 9 ----- src/EFCore/Metadata/Internal/Model.cs | 38 +++++++++++++++++++ src/EFCore/Update/Internal/UpdateAdapter.cs | 2 +- 9 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/EFCore/ChangeTracking/ChangeTracker.cs b/src/EFCore/ChangeTracking/ChangeTracker.cs index da8aa7835b4..25d68233002 100644 --- a/src/EFCore/ChangeTracking/ChangeTracker.cs +++ b/src/EFCore/ChangeTracking/ChangeTracker.cs @@ -220,7 +220,7 @@ public virtual bool HasChanges() /// public virtual void DetectChanges() { - if ((string)_model[CoreAnnotationNames.SkipDetectChangesAnnotation] != "true") + if (!((Model)_model).SkipDetectChanges) { ChangeDetector.DetectChanges(StateManager); } diff --git a/src/EFCore/ChangeTracking/CollectionEntry.cs b/src/EFCore/ChangeTracking/CollectionEntry.cs index c27e6332479..96af0410be0 100644 --- a/src/EFCore/ChangeTracking/CollectionEntry.cs +++ b/src/EFCore/ChangeTracking/CollectionEntry.cs @@ -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; diff --git a/src/EFCore/ChangeTracking/EntityEntry.cs b/src/EFCore/ChangeTracking/EntityEntry.cs index 5e932a4cebf..16d917f2ce7 100644 --- a/src/EFCore/ChangeTracking/EntityEntry.cs +++ b/src/EFCore/ChangeTracking/EntityEntry.cs @@ -102,7 +102,7 @@ public virtual EntityState State /// public virtual void DetectChanges() { - if ((string)Context.Model[CoreAnnotationNames.SkipDetectChangesAnnotation] != "true") + if (!((Model)Context.Model).SkipDetectChanges) { Context.GetDependencies().ChangeDetector.DetectChanges(InternalEntry); } diff --git a/src/EFCore/ChangeTracking/Internal/StateManager.cs b/src/EFCore/ChangeTracking/Internal/StateManager.cs index 8e257c1086a..2b0263d8489 100644 --- a/src/EFCore/ChangeTracking/Internal/StateManager.cs +++ b/src/EFCore/ChangeTracking/Internal/StateManager.cs @@ -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; diff --git a/src/EFCore/ChangeTracking/ReferenceEntry.cs b/src/EFCore/ChangeTracking/ReferenceEntry.cs index 93cc1cb5010..eddcbcb4f4f 100644 --- a/src/EFCore/ChangeTracking/ReferenceEntry.cs +++ b/src/EFCore/ChangeTracking/ReferenceEntry.cs @@ -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); } diff --git a/src/EFCore/Metadata/Conventions/ChangeTrackingStrategyConvention.cs b/src/EFCore/Metadata/Conventions/ChangeTrackingStrategyConvention.cs index 0a4ae96eefe..6370809532e 100644 --- a/src/EFCore/Metadata/Conventions/ChangeTrackingStrategyConvention.cs +++ b/src/EFCore/Metadata/Conventions/ChangeTrackingStrategyConvention.cs @@ -41,7 +41,10 @@ public virtual void ProcessModelFinalizing( } } - modelBuilder.HasAnnotation(CoreAnnotationNames.SkipDetectChangesAnnotation, "true"); + if (modelBuilder.Metadata is Model model) + { + model.SetSkipDetectChanges(true, ConfigurationSource.Convention); + } } } } diff --git a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs index e170f185413..7dc0c460b3f 100644 --- a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs +++ b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs @@ -257,14 +257,6 @@ public static class CoreAnnotationNames /// public const string AmbiguousField = "BackingFieldConvention:AmbiguousField"; - /// - /// 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. - /// - public const string SkipDetectChangesAnnotation = "ChangeDetector.SkipDetectChanges"; - /// /// 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 @@ -315,7 +307,6 @@ public static class CoreAnnotationNames AmbiguousNavigations, DuplicateServiceProperties, AmbiguousField, - SkipDetectChangesAnnotation, SkipChangeTrackingStrategyValidationAnnotation }; } diff --git a/src/EFCore/Metadata/Internal/Model.cs b/src/EFCore/Metadata/Internal/Model.cs index c19e5eb98e4..dcf38fd5699 100644 --- a/src/EFCore/Metadata/Internal/Model.cs +++ b/src/EFCore/Metadata/Internal/Model.cs @@ -61,6 +61,9 @@ private readonly Dictionary _ignoredTypeNames private readonly Dictionary _sharedTypes = new Dictionary { { DefaultPropertyBagType, ConfigurationSource.Convention } }; + private bool? _skipDetectChanges; + private ConfigurationSource? _skipDetectChangesConfigurationSource; + /// /// 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 @@ -989,6 +992,41 @@ private IModel MakeReadonly() public virtual PropertyInfo FindIndexerPropertyInfo([NotNull] Type type) => _indexerPropertyInfoMap.GetOrAdd(type, type.FindIndexerProperty()); + /// + /// 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. + /// + public virtual bool SkipDetectChanges + { + get => _skipDetectChanges ?? false; + set => SetSkipDetectChanges(value, ConfigurationSource.Explicit); + } + + /// + /// 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. + /// + 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; + } + /// /// 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 diff --git a/src/EFCore/Update/Internal/UpdateAdapter.cs b/src/EFCore/Update/Internal/UpdateAdapter.cs index d84674ff158..963e5c90907 100644 --- a/src/EFCore/Update/Internal/UpdateAdapter.cs +++ b/src/EFCore/Update/Internal/UpdateAdapter.cs @@ -115,7 +115,7 @@ public virtual IEnumerable Entries /// public virtual void DetectChanges() { - if ((string)_stateManager.Model[CoreAnnotationNames.SkipDetectChangesAnnotation] != "true") + if (!((Model)_stateManager.Model).SkipDetectChanges) { _changeDetector.DetectChanges(_stateManager); }