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(); }