diff --git a/src/EFCore.Abstractions/Infrastructure/ILazyLoader.cs b/src/EFCore.Abstractions/Infrastructure/ILazyLoader.cs
index ad0ec5aa5ca..6974d2c11ff 100644
--- a/src/EFCore.Abstractions/Infrastructure/ILazyLoader.cs
+++ b/src/EFCore.Abstractions/Infrastructure/ILazyLoader.cs
@@ -20,7 +20,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure;
/// See Lazy loading for more information and examples.
///
///
-public interface ILazyLoader : IDisposable
+public interface ILazyLoader
{
///
/// Sets the given navigation as known to be completely loaded or known to be
@@ -66,4 +66,9 @@ Task LoadAsync(
object entity,
CancellationToken cancellationToken = default,
[CallerMemberName] string navigationName = "");
+
+ ///
+ /// Disposes the loader.
+ ///
+ void Dispose();
}
diff --git a/src/EFCore/ChangeTracking/Internal/StateManager.cs b/src/EFCore/ChangeTracking/Internal/StateManager.cs
index b072ae32de6..eb42067b0af 100644
--- a/src/EFCore/ChangeTracking/Internal/StateManager.cs
+++ b/src/EFCore/ChangeTracking/Internal/StateManager.cs
@@ -695,6 +695,11 @@ public virtual void Unsubscribe(bool resetting)
{
disposable.Dispose();
}
+ else if (resetting
+ && service is ILazyLoader lazyLoader)
+ {
+ lazyLoader.Dispose();
+ }
else if (service is not IInjectableService detachable
|| detachable.Detaching(Context, entry.Entity))
{
diff --git a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
index ff4b7b79262..3311f7e1fbd 100644
--- a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
+++ b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
@@ -126,6 +126,7 @@ public static readonly IDictionary CoreServices
{ typeof(IDbContextLogger), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IAdHocMapper), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(ILazyLoader), new ServiceCharacteristics(ServiceLifetime.Transient) },
+ { typeof(ILazyLoaderFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IParameterBindingFactory), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(ITypeMappingSourcePlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{
@@ -285,12 +286,14 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd(p => new DesignTimeModel(GetContextServices(p)));
TryAdd(p => GetContextServices(p).CurrentContext);
TryAdd(p => GetContextServices(p).ContextOptions);
+ TryAdd(p => p.GetRequiredService());
TryAdd(p => p.GetRequiredService());
TryAdd(p => p.GetRequiredService());
TryAdd();
TryAdd();
TryAdd();
- TryAdd();
+ TryAdd();
+ TryAdd(p => p.GetRequiredService().Create());
TryAdd();
TryAdd();
TryAdd();
diff --git a/src/EFCore/Infrastructure/Internal/ILazyLoaderFactory.cs b/src/EFCore/Infrastructure/Internal/ILazyLoaderFactory.cs
new file mode 100644
index 00000000000..5dc41dbb17a
--- /dev/null
+++ b/src/EFCore/Infrastructure/Internal/ILazyLoaderFactory.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Infrastructure.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.
+///
+public interface ILazyLoaderFactory : IDisposable, IResettableService
+{
+ ///
+ /// 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.
+ ///
+ ILazyLoader Create();
+}
diff --git a/src/EFCore/Infrastructure/Internal/LazyLoaderFactory.cs b/src/EFCore/Infrastructure/Internal/LazyLoaderFactory.cs
new file mode 100644
index 00000000000..86f51ce973d
--- /dev/null
+++ b/src/EFCore/Infrastructure/Internal/LazyLoaderFactory.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Infrastructure.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.
+///
+public class LazyLoaderFactory : ILazyLoaderFactory
+{
+ private readonly ICurrentDbContext _currentContext;
+ private readonly IDiagnosticsLogger _logger;
+ private readonly List _loaders = new();
+
+ ///
+ /// 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 LazyLoaderFactory(
+ ICurrentDbContext currentContext,
+ IDiagnosticsLogger logger)
+ {
+ _currentContext = currentContext;
+ _logger = logger;
+ }
+
+ ///
+ /// 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 ILazyLoader Create()
+ {
+ var loader = new LazyLoader(_currentContext, _logger);
+ _loaders.Add(loader);
+ return loader;
+ }
+
+ ///
+ /// 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 void Dispose()
+ {
+ foreach (var loader in _loaders)
+ {
+ loader.Dispose();
+ }
+ _loaders.Clear();
+ }
+
+ ///
+ /// 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 void ResetState()
+ => Dispose();
+
+ ///
+ /// 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 Task ResetStateAsync(CancellationToken cancellationToken = default)
+ {
+ Dispose();
+
+ return Task.CompletedTask;
+ }
+}
diff --git a/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs b/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs
index 9e961fe0aff..d7435418f1f 100644
--- a/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs
+++ b/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs
@@ -266,7 +266,7 @@ private static void TestMultipleScoped(Action tr
{
services = context.GetService>().ToList();
- Assert.Equal(3, services.Count);
+ Assert.Equal(4, services.Count);
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()));
@@ -281,7 +281,7 @@ private static void TestMultipleScoped(Action tr
{
var newServices = context.GetService>().ToList();
- Assert.Equal(3, newServices.Count);
+ Assert.Equal(4, newServices.Count);
foreach (var service in newServices)
{