diff --git a/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs b/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs
new file mode 100644
index 00000000000..d0f12680162
--- /dev/null
+++ b/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs
@@ -0,0 +1,29 @@
+// 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;
+using JetBrains.Annotations;
+
+namespace Microsoft.EntityFrameworkCore.Infrastructure
+{
+ ///
+ /// A returned by an , which will exit the ongoing
+ /// critical section when disposed.
+ ///
+ public readonly struct ConcurrencyDetectorCriticalSectionDisposer : IDisposable
+ {
+ private readonly IConcurrencyDetector _concurrencyDetector;
+
+ ///
+ /// Constructs a new .
+ ///
+ ///
+ /// The on which the critical section will be exited.
+ ///
+ public ConcurrencyDetectorCriticalSectionDisposer([NotNull] IConcurrencyDetector concurrencyDetector)
+ => _concurrencyDetector = concurrencyDetector;
+
+ ///
+ public void Dispose() => _concurrencyDetector.ExitCriticalSection();
+ }
+}
diff --git a/src/EFCore/Infrastructure/IConcurrencyDetector.cs b/src/EFCore/Infrastructure/IConcurrencyDetector.cs
index b1f194b4496..95c87514cf9 100644
--- a/src/EFCore/Infrastructure/IConcurrencyDetector.cs
+++ b/src/EFCore/Infrastructure/IConcurrencyDetector.cs
@@ -21,9 +21,14 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure
public interface IConcurrencyDetector
{
///
- /// Call to enter the critical section.
+ /// Enters a critical section.
///
/// A disposer that will exit the critical section when disposed.
- IDisposable EnterCriticalSection();
+ ConcurrencyDetectorCriticalSectionDisposer EnterCriticalSection();
+
+ ///
+ /// Exits the critical section.
+ ///
+ void ExitCriticalSection();
}
}
diff --git a/src/EFCore/Internal/ConcurrencyDetector.cs b/src/EFCore/Internal/ConcurrencyDetector.cs
index c26e7835574..c58c06af051 100644
--- a/src/EFCore/Internal/ConcurrencyDetector.cs
+++ b/src/EFCore/Internal/ConcurrencyDetector.cs
@@ -26,7 +26,6 @@ namespace Microsoft.EntityFrameworkCore.Internal
///
public class ConcurrencyDetector : IConcurrencyDetector
{
- private readonly IDisposable _disposer;
private int _inCriticalSection;
private static readonly AsyncLocal _threadHasLock = new AsyncLocal();
private int _refCount;
@@ -37,15 +36,7 @@ public class ConcurrencyDetector : IConcurrencyDetector
/// 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 ConcurrencyDetector() => _disposer = new Disposer(this);
-
- ///
- /// 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 IDisposable EnterCriticalSection()
+ public virtual ConcurrencyDetectorCriticalSectionDisposer EnterCriticalSection()
{
if (Interlocked.CompareExchange(ref _inCriticalSection, 1, 0) == 1)
{
@@ -60,10 +51,16 @@ public virtual IDisposable EnterCriticalSection()
}
_refCount++;
- return _disposer;
+ return new ConcurrencyDetectorCriticalSectionDisposer(this);
}
- private void ExitCriticalSection()
+ ///
+ /// 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 void ExitCriticalSection()
{
Check.DebugAssert(_inCriticalSection == 1, "Expected to be in a critical section");
@@ -73,15 +70,5 @@ private void ExitCriticalSection()
_inCriticalSection = 0;
}
}
-
- private readonly struct Disposer : IDisposable
- {
- private readonly ConcurrencyDetector _concurrencyDetector;
-
- public Disposer(ConcurrencyDetector concurrencyDetector)
- => _concurrencyDetector = concurrencyDetector;
-
- public void Dispose() => _concurrencyDetector.ExitCriticalSection();
- }
}
}
diff --git a/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs b/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs
index adab826b9e7..d9a2914ced2 100644
--- a/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs
+++ b/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs
@@ -316,12 +316,12 @@ private static void TestMultipleScoped(Action tr
private class FakeConcurrencyDetector : IConcurrencyDetector
{
- public IDisposable EnterCriticalSection()
+ ConcurrencyDetectorCriticalSectionDisposer IConcurrencyDetector.EnterCriticalSection()
{
throw new NotImplementedException();
}
- public Task EnterCriticalSectionAsync(CancellationToken cancellationToken)
+ public void ExitCriticalSection()
{
throw new NotImplementedException();
}