diff --git a/src/EFCore/ChangeTracking/Internal/IChangeDetector.cs b/src/EFCore/ChangeTracking/Internal/IChangeDetector.cs
index d35990905ba..2246602fbd7 100644
--- a/src/EFCore/ChangeTracking/Internal/IChangeDetector.cs
+++ b/src/EFCore/ChangeTracking/Internal/IChangeDetector.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
@@ -20,8 +21,24 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
/// The implementation does not need to be thread-safe.
///
///
- public interface IChangeDetector : IPropertyListener
+ public interface IChangeDetector
{
+ ///
+ /// 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.
+ ///
+ void PropertyChanged([NotNull] InternalEntityEntry entry, [NotNull] IPropertyBase propertyBase, bool setModified);
+
+ ///
+ /// 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.
+ ///
+ void PropertyChanging([NotNull] InternalEntityEntry entry, [NotNull] IPropertyBase propertyBase);
+
///
/// 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/ChangeTracking/Internal/IEntityStateListener.cs b/src/EFCore/ChangeTracking/Internal/IEntityStateListener.cs
deleted file mode 100644
index 427d86147a8..00000000000
--- a/src/EFCore/ChangeTracking/Internal/IEntityStateListener.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using JetBrains.Annotations;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
-{
- // TODO: Consider which of listeners/events/interceptors/etc is better here
- // See issue #737
- ///
- ///
- /// 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.
- ///
- ///
- /// The service lifetime is and multiple registrations
- /// are allowed. This means that each instance will use its own
- /// set of instances of this service.
- /// The implementations may depend on other services registered with any lifetime.
- /// The implementations do not need to be thread-safe.
- ///
- ///
- public interface IEntityStateListener
- {
- ///
- /// 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.
- ///
- void StateChanging([NotNull] InternalEntityEntry entry, EntityState newState);
-
- ///
- /// 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.
- ///
- void StateChanged(
- [NotNull] InternalEntityEntry entry,
- EntityState oldState,
- bool fromQuery);
- }
-}
diff --git a/src/EFCore/ChangeTracking/Internal/IKeyListener.cs b/src/EFCore/ChangeTracking/Internal/IKeyListener.cs
deleted file mode 100644
index b275dacaddb..00000000000
--- a/src/EFCore/ChangeTracking/Internal/IKeyListener.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Generic;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
-{
- ///
- ///
- /// 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.
- ///
- ///
- /// The service lifetime is and multiple registrations
- /// are allowed. This means that each instance will use its own
- /// set of instances of this service.
- /// The implementations may depend on other services registered with any lifetime.
- /// The implementations do not need to be thread-safe.
- ///
- ///
- public interface IKeyListener
- {
- ///
- /// 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.
- ///
- void KeyPropertyChanged(
- [NotNull] InternalEntityEntry entry,
- [NotNull] IProperty property,
- [NotNull] IReadOnlyList containingPrincipalKeys,
- [NotNull] IReadOnlyList containingForeignKeys,
- [CanBeNull] object oldValue,
- [CanBeNull] object newValue);
- }
-}
diff --git a/src/EFCore/ChangeTracking/Internal/ILocalViewListener.cs b/src/EFCore/ChangeTracking/Internal/ILocalViewListener.cs
index 3a699bd76dc..8a11d3abfa6 100644
--- a/src/EFCore/ChangeTracking/Internal/ILocalViewListener.cs
+++ b/src/EFCore/ChangeTracking/Internal/ILocalViewListener.cs
@@ -21,7 +21,7 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
/// The implementation does not need to be thread-safe.
///
///
- public interface ILocalViewListener : IEntityStateListener
+ public interface ILocalViewListener
{
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -30,5 +30,24 @@ public interface ILocalViewListener : IEntityStateListener
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
void RegisterView([NotNull] Action viewAction);
+
+ ///
+ /// 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.
+ ///
+ void StateChanging([NotNull] InternalEntityEntry entry, EntityState newState);
+
+ ///
+ /// 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.
+ ///
+ void StateChanged(
+ [NotNull] InternalEntityEntry entry,
+ EntityState oldState,
+ bool fromQuery);
}
}
diff --git a/src/EFCore/ChangeTracking/Internal/INavigationFixer.cs b/src/EFCore/ChangeTracking/Internal/INavigationFixer.cs
index 77e6aa13897..4343261a430 100644
--- a/src/EFCore/ChangeTracking/Internal/INavigationFixer.cs
+++ b/src/EFCore/ChangeTracking/Internal/INavigationFixer.cs
@@ -1,6 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System.Collections.Generic;
+using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
@@ -19,7 +22,73 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
/// The implementation does not need to be thread-safe.
///
///
- public interface INavigationFixer : IEntityStateListener, INavigationListener, IKeyListener, IQueryTrackingListener
+ public interface INavigationFixer
{
+ ///
+ /// 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.
+ ///
+ void NavigationReferenceChanged(
+ [NotNull] InternalEntityEntry entry,
+ [NotNull] INavigation navigation,
+ [CanBeNull] object oldValue,
+ [CanBeNull] object newValue);
+
+ ///
+ /// 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.
+ ///
+ void NavigationCollectionChanged(
+ [NotNull] InternalEntityEntry entry,
+ [NotNull] INavigation navigation,
+ [NotNull] IEnumerable added,
+ [NotNull] IEnumerable removed);
+
+ ///
+ /// 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.
+ ///
+ void TrackedFromQuery(
+ [NotNull] InternalEntityEntry entry,
+ [CanBeNull] ISet handledForeignKeys);
+
+ ///
+ /// 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.
+ ///
+ void KeyPropertyChanged(
+ [NotNull] InternalEntityEntry entry,
+ [NotNull] IProperty property,
+ [NotNull] IReadOnlyList containingPrincipalKeys,
+ [NotNull] IReadOnlyList containingForeignKeys,
+ [CanBeNull] object oldValue,
+ [CanBeNull] object newValue);
+
+ ///
+ /// 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.
+ ///
+ void StateChanging([NotNull] InternalEntityEntry entry, EntityState newState);
+
+ ///
+ /// 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.
+ ///
+ void StateChanged(
+ [NotNull] InternalEntityEntry entry,
+ EntityState oldState,
+ bool fromQuery);
}
}
diff --git a/src/EFCore/ChangeTracking/Internal/INavigationListener.cs b/src/EFCore/ChangeTracking/Internal/INavigationListener.cs
deleted file mode 100644
index 94bfc78ccd7..00000000000
--- a/src/EFCore/ChangeTracking/Internal/INavigationListener.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Generic;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
-{
- ///
- ///
- /// 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.
- ///
- ///
- /// The service lifetime is and multiple registrations
- /// are allowed. This means that each instance will use its own
- /// set of instances of this service.
- /// The implementations may depend on other services registered with any lifetime.
- /// The implementations do not need to be thread-safe.
- ///
- ///
- public interface INavigationListener
- {
- ///
- /// 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.
- ///
- void NavigationReferenceChanged(
- [NotNull] InternalEntityEntry entry,
- [NotNull] INavigation navigation,
- [CanBeNull] object oldValue,
- [CanBeNull] object newValue);
-
- ///
- /// 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.
- ///
- void NavigationCollectionChanged(
- [NotNull] InternalEntityEntry entry,
- [NotNull] INavigation navigation,
- [NotNull] IEnumerable added,
- [NotNull] IEnumerable removed);
- }
-}
diff --git a/src/EFCore/ChangeTracking/Internal/IPropertyListener.cs b/src/EFCore/ChangeTracking/Internal/IPropertyListener.cs
deleted file mode 100644
index 8a4db28bb81..00000000000
--- a/src/EFCore/ChangeTracking/Internal/IPropertyListener.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
-{
- ///
- ///
- /// 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.
- ///
- ///
- /// The service lifetime is and multiple registrations
- /// are allowed. This means that each instance will use its own
- /// set of instances of this service.
- /// The implementations may depend on other services registered with any lifetime.
- /// The implementations do not need to be thread-safe.
- ///
- ///
- public interface IPropertyListener
- {
- ///
- /// 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.
- ///
- void PropertyChanged([NotNull] InternalEntityEntry entry, [NotNull] IPropertyBase propertyBase, bool setModified);
-
- ///
- /// 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.
- ///
- void PropertyChanging([NotNull] InternalEntityEntry entry, [NotNull] IPropertyBase propertyBase);
- }
-}
diff --git a/src/EFCore/ChangeTracking/Internal/IQueryTrackingListener.cs b/src/EFCore/ChangeTracking/Internal/IQueryTrackingListener.cs
deleted file mode 100644
index 85d6c45050d..00000000000
--- a/src/EFCore/ChangeTracking/Internal/IQueryTrackingListener.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Generic;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
-{
- ///
- ///
- /// 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.
- ///
- ///
- /// The service lifetime is and multiple registrations
- /// are allowed. This means that each instance will use its own
- /// set of instances of this service.
- /// The implementations may depend on other services registered with any lifetime.
- /// The implementations do not need to be thread-safe.
- ///
- ///
- public interface IQueryTrackingListener
- {
- ///
- /// 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.
- ///
- void TrackedFromQuery(
- [NotNull] InternalEntityEntry entry,
- [CanBeNull] ISet handledForeignKeys);
- }
-}
diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntryNotifier.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntryNotifier.cs
index 4fc9622f245..b1daacd50fe 100644
--- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntryNotifier.cs
+++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntryNotifier.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
-using System.Linq;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.DependencyInjection;
@@ -25,11 +24,9 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
///
public class InternalEntityEntryNotifier : IInternalEntityEntryNotifier
{
- private readonly IQueryTrackingListener[] _queryTrackingListeners;
- private readonly IEntityStateListener[] _entityStateListeners;
- private readonly IPropertyListener[] _propertyListeners;
- private readonly INavigationListener[] _navigationListeners;
- private readonly IKeyListener[] _keyListeners;
+ private readonly ILocalViewListener _localViewListener;
+ private readonly IChangeDetector _changeDetector;
+ private readonly INavigationFixer _navigationFixer;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -38,41 +35,13 @@ public class InternalEntityEntryNotifier : IInternalEntityEntryNotifier
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public InternalEntityEntryNotifier(
- [CanBeNull] IEnumerable entityStateListeners,
- [CanBeNull] IEnumerable propertyListeners,
- [CanBeNull] IEnumerable navigationListeners,
- [CanBeNull] IEnumerable keyListeners,
- [CanBeNull] IEnumerable queryTrackingListeners)
+ [CanBeNull] ILocalViewListener localViewListener,
+ [CanBeNull] IChangeDetector changeDetector,
+ [CanBeNull] INavigationFixer navigationFixer)
{
- if (entityStateListeners != null)
- {
- var listeners = entityStateListeners.ToArray();
- _entityStateListeners = listeners.Length == 0 ? null : listeners;
- }
-
- if (propertyListeners != null)
- {
- var listeners = propertyListeners.ToArray();
- _propertyListeners = listeners.Length == 0 ? null : listeners;
- }
-
- if (navigationListeners != null)
- {
- var listeners = navigationListeners.ToArray();
- _navigationListeners = listeners.Length == 0 ? null : listeners;
- }
-
- if (keyListeners != null)
- {
- var listeners = keyListeners.ToArray();
- _keyListeners = listeners.Length == 0 ? null : listeners;
- }
-
- if (queryTrackingListeners != null)
- {
- var listeners = queryTrackingListeners.ToArray();
- _queryTrackingListeners = listeners.Length == 0 ? null : listeners;
- }
+ _localViewListener = localViewListener;
+ _changeDetector = changeDetector;
+ _navigationFixer = navigationFixer;
}
///
@@ -83,15 +52,8 @@ public InternalEntityEntryNotifier(
///
public virtual void StateChanging(InternalEntityEntry entry, EntityState newState)
{
- if (_entityStateListeners == null)
- {
- return;
- }
-
- foreach (var listener in _entityStateListeners)
- {
- listener.StateChanging(entry, newState);
- }
+ _navigationFixer.StateChanging(entry, newState);
+ _localViewListener.StateChanging(entry, newState);
}
///
@@ -102,15 +64,8 @@ public virtual void StateChanging(InternalEntityEntry entry, EntityState newStat
///
public virtual void StateChanged(InternalEntityEntry entry, EntityState oldState, bool fromQuery)
{
- if (_entityStateListeners == null)
- {
- return;
- }
-
- foreach (var listener in _entityStateListeners)
- {
- listener.StateChanged(entry, oldState, fromQuery);
- }
+ _navigationFixer.StateChanged(entry, oldState, fromQuery);
+ _localViewListener.StateChanged(entry, oldState, fromQuery);
}
///
@@ -122,17 +77,7 @@ public virtual void StateChanged(InternalEntityEntry entry, EntityState oldState
public virtual void TrackedFromQuery(
InternalEntityEntry entry,
ISet handledForeignKeys)
- {
- if (_entityStateListeners == null)
- {
- return;
- }
-
- foreach (var listener in _queryTrackingListeners)
- {
- listener.TrackedFromQuery(entry, handledForeignKeys);
- }
- }
+ => _navigationFixer.TrackedFromQuery(entry, handledForeignKeys);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -145,17 +90,7 @@ public virtual void NavigationReferenceChanged(
INavigation navigation,
object oldValue,
object newValue)
- {
- if (_navigationListeners == null)
- {
- return;
- }
-
- foreach (var listener in _navigationListeners)
- {
- listener.NavigationReferenceChanged(entry, navigation, oldValue, newValue);
- }
- }
+ => _navigationFixer.NavigationReferenceChanged(entry, navigation, oldValue, newValue);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -168,17 +103,7 @@ public virtual void NavigationCollectionChanged(
INavigation navigation,
IEnumerable added,
IEnumerable removed)
- {
- if (_navigationListeners == null)
- {
- return;
- }
-
- foreach (var listener in _navigationListeners)
- {
- listener.NavigationCollectionChanged(entry, navigation, added, removed);
- }
- }
+ => _navigationFixer.NavigationCollectionChanged(entry, navigation, added, removed);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -193,17 +118,7 @@ public virtual void KeyPropertyChanged(
IReadOnlyList foreignKeys,
object oldValue,
object newValue)
- {
- if (_keyListeners == null)
- {
- return;
- }
-
- foreach (var listener in _keyListeners)
- {
- listener.KeyPropertyChanged(entry, property, keys, foreignKeys, oldValue, newValue);
- }
- }
+ => _navigationFixer.KeyPropertyChanged(entry, property, keys, foreignKeys, oldValue, newValue);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -212,17 +127,7 @@ public virtual void KeyPropertyChanged(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual void PropertyChanged(InternalEntityEntry entry, IPropertyBase property, bool setModified)
- {
- if (_propertyListeners == null)
- {
- return;
- }
-
- foreach (var listener in _propertyListeners)
- {
- listener.PropertyChanged(entry, property, setModified);
- }
- }
+ => _changeDetector.PropertyChanged(entry, property, setModified);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -231,16 +136,6 @@ public virtual void PropertyChanged(InternalEntityEntry entry, IPropertyBase pro
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual void PropertyChanging(InternalEntityEntry entry, IPropertyBase property)
- {
- if (_propertyListeners == null)
- {
- return;
- }
-
- foreach (var listener in _propertyListeners)
- {
- listener.PropertyChanging(entry, property);
- }
- }
+ => _changeDetector.PropertyChanging(entry, property);
}
}
diff --git a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
index f444b76e348..59a1d271c7d 100644
--- a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
+++ b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
@@ -149,11 +149,6 @@ public static readonly IDictionary CoreServices
{ typeof(ITypeMappingSourcePlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(ISingletonOptions), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(IConventionSetCustomizer), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
- { typeof(IEntityStateListener), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
- { typeof(INavigationListener), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
- { typeof(IKeyListener), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
- { typeof(IQueryTrackingListener), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
- { typeof(IPropertyListener), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
{ typeof(IResettableService), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
// New Query related services
@@ -284,12 +279,6 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd(p => GetContextServices(p).Model);
TryAdd(p => GetContextServices(p).CurrentContext);
TryAdd(p => GetContextServices(p).ContextOptions);
- TryAdd(p => p.GetService());
- TryAdd(p => p.GetService());
- TryAdd(p => p.GetService());
- TryAdd(p => p.GetService());
- TryAdd(p => p.GetService());
- TryAdd(p => p.GetService());
TryAdd(p => p.GetService());
TryAdd(p => p.GetService());
TryAdd();
diff --git a/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs
index 769958f695b..052fbf66223 100644
--- a/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs
+++ b/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs
@@ -2369,8 +2369,7 @@ private static IServiceProvider CreateContextServices(IModel model = null)
new ServiceCollection()
.AddScoped()
.AddScoped()
- .AddScoped(p => p.GetRequiredService())
- .AddScoped(p => p.GetRequiredService()),
+ .AddScoped(p => p.GetRequiredService()),
model ?? BuildModel());
}
@@ -2395,8 +2394,13 @@ public override void AttachGraph(
}
}
- private class TestRelationshipListener : INavigationListener, IKeyListener
+ private class TestRelationshipListener : NavigationFixer
{
+ public TestRelationshipListener(IChangeDetector changeDetector, IEntityGraphAttacher attacher)
+ : base(changeDetector, attacher)
+ {
+ }
+
public Tuple, IReadOnlyList, object, object> KeyChange
{
get;
@@ -2406,18 +2410,22 @@ public Tuple, IReadOnlyList<
public Tuple ReferenceChange { get; set; }
public Tuple, IEnumerable> CollectionChange { get; set; }
- public void NavigationReferenceChanged(InternalEntityEntry entry, INavigation navigation, object oldValue, object newValue)
+ public override void NavigationReferenceChanged(InternalEntityEntry entry, INavigation navigation, object oldValue, object newValue)
{
ReferenceChange = Tuple.Create(entry, navigation, oldValue, newValue);
+
+ base.NavigationReferenceChanged(entry, navigation, oldValue, newValue);
}
- public void NavigationCollectionChanged(
+ public override void NavigationCollectionChanged(
InternalEntityEntry entry, INavigation navigation, IEnumerable added, IEnumerable removed)
{
CollectionChange = Tuple.Create(entry, navigation, added, removed);
+
+ base.NavigationCollectionChanged(entry, navigation, added, removed);
}
- public void KeyPropertyChanged(
+ public override void KeyPropertyChanged(
InternalEntityEntry entry,
IProperty property,
IReadOnlyList containingPrincipalKeys,
@@ -2426,6 +2434,8 @@ public void KeyPropertyChanged(
object newValue)
{
KeyChange = Tuple.Create(entry, property, containingPrincipalKeys, containingForeignKeys, oldValue, newValue);
+
+ base.KeyPropertyChanged(entry, property, containingPrincipalKeys, containingForeignKeys, oldValue, newValue);
}
}
}
diff --git a/test/EFCore.Tests/ChangeTracking/Internal/InternalEntryEntrySubscriberTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/InternalEntryEntrySubscriberTest.cs
index 68e35904b93..b380b12489b 100644
--- a/test/EFCore.Tests/ChangeTracking/Internal/InternalEntryEntrySubscriberTest.cs
+++ b/test/EFCore.Tests/ChangeTracking/Internal/InternalEntryEntrySubscriberTest.cs
@@ -227,11 +227,11 @@ private static TestNavigationListener SetupTestCollectionListener(
ICollection collection)
{
var contextServices = InMemoryTestHelpers.Instance.CreateContextServices(
- new ServiceCollection().AddScoped(),
+ new ServiceCollection().AddScoped(),
BuildModel());
var testListener = contextServices
- .GetRequiredService>()
+ .GetRequiredService>()
.OfType()
.Single();
@@ -249,10 +249,10 @@ private static TestNavigationListener SetupTestCollectionListener(
public void Entry_subscribes_to_INotifyPropertyChanging_and_INotifyPropertyChanged_for_properties()
{
var contextServices = InMemoryTestHelpers.Instance.CreateContextServices(
- new ServiceCollection().AddScoped(),
+ new ServiceCollection().AddScoped(),
BuildModel());
- var testListener = contextServices.GetRequiredService>().OfType().Single();
+ var testListener = contextServices.GetRequiredService>().OfType().Single();
var entity = new FullNotificationEntity();
var entry = contextServices.GetRequiredService().GetOrCreateEntry(entity);
@@ -272,10 +272,10 @@ public void Entry_subscribes_to_INotifyPropertyChanging_and_INotifyPropertyChang
public void Entry_handles_null_or_empty_string_in_INotifyPropertyChanging_and_INotifyPropertyChanged()
{
var contextServices = InMemoryTestHelpers.Instance.CreateContextServices(
- new ServiceCollection().AddScoped(),
+ new ServiceCollection().AddScoped(),
BuildModel());
- var testListener = contextServices.GetRequiredService>().OfType().Single();
+ var testListener = contextServices.GetRequiredService>().OfType().Single();
var entity = new FullNotificationEntity();
var entry = contextServices.GetRequiredService().GetOrCreateEntry(entity);
@@ -303,10 +303,10 @@ public void Entry_handles_null_or_empty_string_in_INotifyPropertyChanging_and_IN
public void Entry_subscribes_to_INotifyPropertyChanging_and_INotifyPropertyChanged_for_navigations()
{
var contextServices = InMemoryTestHelpers.Instance.CreateContextServices(
- new ServiceCollection().AddScoped(),
+ new ServiceCollection().AddScoped(),
BuildModel());
- var testListener = contextServices.GetRequiredService>().OfType().Single();
+ var testListener = contextServices.GetRequiredService>().OfType().Single();
var entity = new FullNotificationEntity();
var entry = contextServices.GetRequiredService().GetOrCreateEntry(entity);
@@ -326,10 +326,10 @@ public void Entry_subscribes_to_INotifyPropertyChanging_and_INotifyPropertyChang
public void Subscriptions_to_INotifyPropertyChanging_and_INotifyPropertyChanged_ignore_unmapped_properties()
{
var contextServices = InMemoryTestHelpers.Instance.CreateContextServices(
- new ServiceCollection().AddScoped(),
+ new ServiceCollection().AddScoped(),
BuildModel());
- var testListener = contextServices.GetRequiredService>().OfType().Single();
+ var testListener = contextServices.GetRequiredService>().OfType().Single();
var entity = new FullNotificationEntity();
contextServices.GetRequiredService().GetOrCreateEntry(entity);
@@ -347,11 +347,11 @@ public void Subscriptions_to_INotifyPropertyChanging_and_INotifyPropertyChanged_
public void Entry_unsubscribes_to_INotifyPropertyChanging_and_INotifyPropertyChanged()
{
var contextServices = InMemoryTestHelpers.Instance.CreateContextServices(
- new ServiceCollection().AddScoped(),
+ new ServiceCollection().AddScoped(),
BuildModel());
var testListener = contextServices
- .GetRequiredService>()
+ .GetRequiredService>()
.OfType().Single();
var entities = new List();
@@ -405,11 +405,11 @@ public void Entry_unsubscribes_to_INotifyPropertyChanging_and_INotifyPropertyCha
public void Entry_unsubscribes_to_INotifyCollectionChanged()
{
var contextServices = InMemoryTestHelpers.Instance.CreateContextServices(
- new ServiceCollection().AddScoped(),
+ new ServiceCollection().AddScoped(),
BuildModel());
var testListener = contextServices
- .GetRequiredService>()
+ .GetRequiredService>()
.OfType()
.Single();
@@ -456,11 +456,11 @@ public void Entry_unsubscribes_to_INotifyCollectionChanged()
public void Entries_are_unsubscribed_when_context_is_disposed()
{
var context = InMemoryTestHelpers.Instance.CreateContext(
- new ServiceCollection().AddScoped(),
+ new ServiceCollection().AddScoped(),
BuildModel());
var testListener = context
- .GetService>()
+ .GetService>()
.OfType().Single();
var entities = new List();
@@ -496,7 +496,7 @@ public void Entries_are_unsubscribed_when_context_is_disposed()
Assert.Equal(2, testListener.Changed.Count);
}
- private class TestPropertyListener : IPropertyListener
+ private class TestPropertyListener : IChangeDetector
{
public List> Changing { get; } =
new List>();
@@ -508,9 +508,25 @@ public void PropertyChanged(InternalEntityEntry entry, IPropertyBase property, b
public void PropertyChanging(InternalEntityEntry entry, IPropertyBase property)
=> Changing.Add(Tuple.Create(entry, property));
+
+ public void DetectChanges(IStateManager stateManager)
+ {
+ }
+
+ public void DetectChanges(InternalEntityEntry entry)
+ {
+ }
+
+ public void Suspend()
+ {
+ }
+
+ public void Resume()
+ {
+ }
}
- private class TestNavigationListener : INavigationListener
+ private class TestNavigationListener : INavigationFixer
{
public List, IEnumerable>> CollectionChanged { get; }
= new List, IEnumerable>>();
@@ -523,6 +539,24 @@ public void NavigationReferenceChanged(
public void NavigationCollectionChanged(
InternalEntityEntry entry, INavigation navigation, IEnumerable added, IEnumerable removed)
=> CollectionChanged.Add(Tuple.Create(entry, navigation, added, removed));
+
+ public void TrackedFromQuery(InternalEntityEntry entry, ISet handledForeignKeys)
+ {
+ }
+
+ public void StateChanging(InternalEntityEntry entry, EntityState newState)
+ {
+ }
+
+ public void StateChanged(InternalEntityEntry entry, EntityState oldState, bool fromQuery)
+ {
+ }
+
+ public void KeyPropertyChanged(
+ InternalEntityEntry entry, IProperty property, IReadOnlyList containingPrincipalKeys, IReadOnlyList containingForeignKeys,
+ object oldValue, object newValue)
+ {
+ }
}
private static IModel BuildModel(
diff --git a/test/EFCore.Tests/ChangeTracking/Internal/StateManagerTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/StateManagerTest.cs
index fc857da219d..1c98b3f5600 100644
--- a/test/EFCore.Tests/ChangeTracking/Internal/StateManagerTest.cs
+++ b/test/EFCore.Tests/ChangeTracking/Internal/StateManagerTest.cs
@@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -824,60 +823,30 @@ public void Can_get_all_entities()
.ToArray());
}
- [Fact]
- public void Listeners_are_notified_when_entity_states_change()
+ private class TestListener : INavigationFixer
{
- var listeners = new[]
- {
- new TestListener(),
- new TestListener(),
- new TestListener()
- };
-
- var services = new ServiceCollection()
- .AddSingleton(listeners[0])
- .AddSingleton(listeners[1])
- .AddSingleton(listeners[2]);
-
- var contextServices = InMemoryTestHelpers.Instance.CreateContextServices(services, BuildModel());
-
- var stateManager = contextServices.GetRequiredService();
-
- var entry = stateManager.GetOrCreateEntry(
- new Category
- {
- Id = 77,
- PrincipalId = 777
- });
- entry.SetEntityState(EntityState.Added);
+ public int ChangingCount;
+ public int ChangedCount;
+ public EntityState ChangingState;
+ public EntityState ChangedState;
- foreach (var listener in listeners)
+ public void NavigationReferenceChanged(InternalEntityEntry entry, INavigation navigation, object oldValue, object newValue)
{
- Assert.Equal(1, listener.ChangingCount);
- Assert.Equal(1, listener.ChangedCount);
-
- Assert.Equal(EntityState.Added, listener.ChangingState);
- Assert.Equal(EntityState.Detached, listener.ChangedState);
}
- entry.SetEntityState(EntityState.Modified);
-
- foreach (var listener in listeners)
+ public void NavigationCollectionChanged(InternalEntityEntry entry, INavigation navigation, IEnumerable added, IEnumerable removed)
{
- Assert.Equal(2, listener.ChangingCount);
- Assert.Equal(2, listener.ChangedCount);
+ }
- Assert.Equal(EntityState.Modified, listener.ChangingState);
- Assert.Equal(EntityState.Added, listener.ChangedState);
+ public void TrackedFromQuery(InternalEntityEntry entry, ISet handledForeignKeys)
+ {
}
- }
- private class TestListener : IEntityStateListener
- {
- public int ChangingCount;
- public int ChangedCount;
- public EntityState ChangingState;
- public EntityState ChangedState;
+ public void KeyPropertyChanged(
+ InternalEntityEntry entry, IProperty property, IReadOnlyList containingPrincipalKeys, IReadOnlyList containingForeignKeys,
+ object oldValue, object newValue)
+ {
+ }
public void StateChanging(InternalEntityEntry entry, EntityState newState)
{
diff --git a/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs b/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs
index bd27f37c66f..eadf56530f2 100644
--- a/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs
+++ b/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs
@@ -8,6 +8,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.InMemory.Storage.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
@@ -189,19 +190,19 @@ private static void TestSingleton(Action tryAdd)
[Fact]
public void Can_register_multiple_scoped_service_with_concrete_implementation()
{
- TestMultipleScoped(b => b.TryAdd());
+ TestMultipleScoped(b => b.TryAdd());
}
[Fact]
public void Can_register_multiple_scoped_service_with_concrete_implementation_non_generic()
{
- TestMultipleScoped(b => b.TryAdd(typeof(IEntityStateListener), typeof(FakeEntityStateListener)));
+ TestMultipleScoped(b => b.TryAdd(typeof(IResettableService), typeof(FakeResetableService)));
}
[Fact]
public void Can_register_multiple_scoped_service_with_full_factory()
{
- TestMultipleScoped(b => b.TryAdd(p => new FakeEntityStateListener()));
+ TestMultipleScoped(b => b.TryAdd(p => new FakeResetableService()));
}
[Fact]
@@ -210,9 +211,9 @@ public void Cannot_register_multiple_scoped_service_with_half_factory()
var builder = new EntityFrameworkServicesBuilder(new ServiceCollection());
Assert.Equal(
- CoreStrings.ImplementationTypeRequired(nameof(IEntityStateListener)),
+ CoreStrings.ImplementationTypeRequired(nameof(IResettableService)),
Assert.Throws(
- () => builder.TryAdd(p => new FakeEntityStateListener()))
+ () => builder.TryAdd(p => new FakeResetableService()))
.Message);
}
@@ -220,7 +221,7 @@ public void Cannot_register_multiple_scoped_service_with_half_factory()
public void Can_register_multiple_scoped_service_with_full_factory_non_generic()
{
TestMultipleScoped(
- b => b.TryAdd(typeof(IEntityStateListener), typeof(FakeEntityStateListener), p => new FakeEntityStateListener()));
+ b => b.TryAdd(typeof(IResettableService), typeof(FakeResetableService), p => new FakeResetableService()));
}
[Fact]
@@ -229,10 +230,10 @@ public void Cannot_register_multiple_scoped_service_with_half_factory_non_generi
var builder = new EntityFrameworkServicesBuilder(new ServiceCollection());
Assert.Equal(
- CoreStrings.ImplementationTypeRequired(nameof(IEntityStateListener)),
+ CoreStrings.ImplementationTypeRequired(nameof(IResettableService)),
Assert.Throws(
() => builder.TryAdd(
- typeof(IEntityStateListener), typeof(IEntityStateListener), p => new FakeEntityStateListener()))
+ typeof(IResettableService), typeof(IResettableService), p => new FakeResetableService()))
.Message);
}
@@ -242,9 +243,9 @@ public void Cannot_register_multiple_scoped_service_with_object_factory()
var builder = new EntityFrameworkServicesBuilder(new ServiceCollection());
Assert.Equal(
- CoreStrings.ImplementationTypeRequired(nameof(IEntityStateListener)),
+ CoreStrings.ImplementationTypeRequired(nameof(IResettableService)),
Assert.Throws(
- () => builder.TryAdd(typeof(IEntityStateListener), typeof(object), p => new FakeEntityStateListener()))
+ () => builder.TryAdd(typeof(IResettableService), typeof(object), p => new FakeResetableService()))
.Message);
}
@@ -254,9 +255,9 @@ public void Cannot_register_multiple_scoped_with_instance()
var builder = new EntityFrameworkServicesBuilder(new ServiceCollection());
Assert.Equal(
- CoreStrings.SingletonRequired("Scoped", nameof(IEntityStateListener)),
+ CoreStrings.SingletonRequired("Scoped", nameof(IResettableService)),
Assert.Throws(
- () => builder.TryAdd(new FakeEntityStateListener()))
+ () => builder.TryAdd(new FakeResetableService()))
.Message);
}
@@ -266,9 +267,9 @@ public void Cannot_register_multiple_scoped_with_instance_non_generic()
var builder = new EntityFrameworkServicesBuilder(new ServiceCollection());
Assert.Equal(
- CoreStrings.SingletonRequired("Scoped", nameof(IEntityStateListener)),
+ CoreStrings.SingletonRequired("Scoped", nameof(IResettableService)),
Assert.Throws(
- () => builder.TryAdd(typeof(IEntityStateListener), new FakeEntityStateListener()))
+ () => builder.TryAdd(typeof(IResettableService), new FakeResetableService()))
.Message);
}
@@ -283,18 +284,18 @@ private static void TestMultipleScoped(Action tr
var serviceProvider = serviceCollection.BuildServiceProvider();
- var services = new List();
+ var services = new List();
using (var context = CreateContext(serviceProvider))
{
- services = context.GetService>().ToList();
+ services = context.GetService>().ToList();
Assert.Equal(3, services.Count);
- Assert.Contains(typeof(FakeEntityStateListener), services.Select(s => s.GetType()));
- Assert.Contains(typeof(NavigationFixer), services.Select(s => s.GetType()));
- Assert.Contains(typeof(LocalViewListener), services.Select(s => s.GetType()));
+ Assert.Contains(typeof(FakeResetableService), services.Select(s => s.GetType()));
+ Assert.Contains(typeof(StateManager), services.Select(s => s.GetType()));
+ Assert.Contains(typeof(InMemoryTransactionManager), services.Select(s => s.GetType()));
- foreach (var service in context.GetService>())
+ foreach (var service in context.GetService>())
{
Assert.Contains(service, services);
}
@@ -302,7 +303,7 @@ private static void TestMultipleScoped(Action tr
using (var context = CreateContext(serviceProvider))
{
- var newServices = context.GetService>().ToList();
+ var newServices = context.GetService>().ToList();
Assert.Equal(3, newServices.Count);
@@ -338,13 +339,9 @@ public DbSet CreateSet(DbContext context)
public object CreateSet(DbContext context, Type type) => throw new NotImplementedException();
}
- private class FakeEntityStateListener : IEntityStateListener
+ private class FakeResetableService : IResettableService
{
- public void StateChanging(InternalEntityEntry entry, EntityState newState)
- {
- }
-
- public void StateChanged(InternalEntityEntry entry, EntityState oldState, bool fromQuery)
+ public void ResetState()
{
}
}
diff --git a/test/EFCore.Tests/Infrastructure/InternalSeviceCollectionMapTest.cs b/test/EFCore.Tests/Infrastructure/InternalSeviceCollectionMapTest.cs
index b63c51f61d9..62ec1726263 100644
--- a/test/EFCore.Tests/Infrastructure/InternalSeviceCollectionMapTest.cs
+++ b/test/EFCore.Tests/Infrastructure/InternalSeviceCollectionMapTest.cs
@@ -248,23 +248,6 @@ public void Can_patch_singleton_service_with_instance_registered_non_generic()
Assert.IsType(Can_patch_singleton_service(serviceMap));
}
- [Fact]
- public virtual void Same_INavigationFixer_is_returned_for_all_registrations()
- {
- using (var context = new DbContext(
- new DbContextOptionsBuilder()
- .UseInternalServiceProvider(InMemoryFixture.DefaultServiceProvider)
- .UseInMemoryDatabase(Guid.NewGuid().ToString()).Options))
- {
- var navFixer = context.GetService();
-
- Assert.Contains(navFixer, context.GetService>());
- Assert.Contains(navFixer, context.GetService>());
- Assert.Contains(navFixer, context.GetService>());
- Assert.Contains(navFixer, context.GetService>());
- }
- }
-
private static FakeSingletonService Can_patch_singleton_service(ServiceCollectionMap serviceMap)
{
var serviceProvider = serviceMap.ServiceCollection.BuildServiceProvider();