From a05bd759e8f73fb63ef7abfc5a101678aafcb2a0 Mon Sep 17 00:00:00 2001 From: Alistair Evans Date: Fri, 18 Oct 2019 17:19:28 +0100 Subject: [PATCH 1/8] Add IAsyncDisposable support to containers and scopes. --- src/Autofac/Autofac.csproj | 13 +- src/Autofac/Core/Disposer.cs | 112 ++++++++++++++- .../Core/DisposerResources.Designer.cs | 82 +++++++++++ src/Autofac/Core/DisposerResources.resx | 126 +++++++++++++++++ src/Autofac/Core/IDisposer.cs | 17 +++ src/Autofac/Core/Resolving/InstanceLookup.cs | 8 ++ src/Autofac/Core/ServiceResources.Designer.cs | 2 - src/Autofac/Util/Disposable.cs | 37 +++++ test/Autofac.Test/Core/DisposerTests.cs | 133 ++++++++++++++++++ test/Autofac.Test/Util/AsyncDisposeTracker.cs | 38 +++++ .../Util/AsyncOnlyDisposeTracker.cs | 26 ++++ test/Autofac.Test/Util/DisposeTracker.cs | 1 + 12 files changed, 585 insertions(+), 10 deletions(-) create mode 100644 src/Autofac/Core/DisposerResources.Designer.cs create mode 100644 src/Autofac/Core/DisposerResources.resx create mode 100644 test/Autofac.Test/Util/AsyncDisposeTracker.cs create mode 100644 test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs diff --git a/src/Autofac/Autofac.csproj b/src/Autofac/Autofac.csproj index ddb8cc2bb..4232b77fc 100644 --- a/src/Autofac/Autofac.csproj +++ b/src/Autofac/Autofac.csproj @@ -3,7 +3,7 @@ Autofac is an IoC container for Microsoft .NET. It manages the dependencies between classes so that applications stay easy to change as they grow in size and complexity. 5.0.0 - netstandard2.0;netstandard1.1;net45 + netcoreapp3.0;netstandard2.0;netstandard1.1;net45 latest $(NoWarn);CS1591;IDE0008 true @@ -150,10 +150,15 @@ True ResolveOperationResources.resx - + + DisposerResources.resx True True + + ServiceResources.resx + True + True True @@ -334,6 +339,10 @@ ResXFileCodeGenerator ResolveOperationResources.Designer.cs + + DisposerResources.Designer.cs + ResXFileCodeGenerator + ResXFileCodeGenerator ServiceResources.Designer.cs diff --git a/src/Autofac/Core/Disposer.cs b/src/Autofac/Core/Disposer.cs index 1567556cf..5b584d296 100644 --- a/src/Autofac/Core/Disposer.cs +++ b/src/Autofac/Core/Disposer.cs @@ -25,6 +25,8 @@ using System; using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Autofac.Util; namespace Autofac.Core @@ -36,11 +38,13 @@ namespace Autofac.Core internal class Disposer : Disposable, IDisposer { /// - /// Contents all implement IDisposable. + /// Contents all implement IDisposable or IAsyncDisposable. /// - private Stack _items = new Stack(); + private Stack _items = new Stack(); - private readonly object _synchRoot = new object(); + // Need to use a semaphore instead of a simple object to lock on, because + // we need to synchronoise an awaitable block. + private SemaphoreSlim _synchRoot = new SemaphoreSlim(1, 1); /// /// Releases unmanaged and - optionally - managed resources. @@ -50,32 +54,128 @@ protected override void Dispose(bool disposing) { if (disposing) { - lock (_synchRoot) + _synchRoot.Wait(); + try { while (_items.Count > 0) { var item = _items.Pop(); - item.Dispose(); + + // If we are in synchronous dispose, and an object doesn't implement + if (item is IDisposable disposable) + { + disposable.Dispose(); + } + else + { + // Type only implements IAsyncDisposable, which is not valid if there is a synchronous dispose being done. + throw new InvalidOperationException(string.Format( + DisposerResources.Culture, + DisposerResources.TypeOnlyImplementsIAsyncDisposable, + item.GetType().FullName)); + } } _items = null; } + finally + { + _synchRoot.Release(); + + // We don't need the semaphore any more. + _synchRoot.Dispose(); + } } base.Dispose(disposing); } +#if NETCOREAPP3_0 + protected override async ValueTask DisposeAsync(bool disposing) + { + if (disposing) + { + // Acquire our semaphore. + await _synchRoot.WaitAsync().ConfigureAwait(false); + try + { + while (_items.Count > 0) + { + var item = _items.Pop(); + + // If the item implements IAsyncDisposable we will call its DisposeAsync Method. + if (item is IAsyncDisposable asyncDisposable) + { + var vt = asyncDisposable.DisposeAsync(); + + // Don't await if it's already completed (this is a slight gain in performance of using ValueTask). + if (!vt.IsCompletedSuccessfully) + { + await vt.ConfigureAwait(false); + } + } + else if (item is IDisposable disposable) + { + // Call the standard Dispose. + disposable.Dispose(); + } + } + + _items = null; + } + finally + { + _synchRoot.Release(); + + // We don't need the semaphore any more. + _synchRoot.Dispose(); + } + } + } + + /// + /// Adds an object to the disposer, where that object only implements IAsyncDisposable. When the disposer is + /// disposed, so will the object be. + /// This is not typically recommended, and you should implement IDisposable as well. + /// + /// The instance. + /// + /// If this Disposer is disposed of using a synchronous Dispose call, that call will throw an exception. + /// + public void AddInstanceForAsyncDisposal(IAsyncDisposable instance) + { + AddInternal(instance); + } +#endif + /// /// Adds an object to the disposer. When the disposer is /// disposed, so will the object be. /// /// The instance. public void AddInstanceForDisposal(IDisposable instance) + { + AddInternal(instance); + } + + private void AddInternal(object instance) { if (instance == null) throw new ArgumentNullException(nameof(instance)); - lock (_synchRoot) + if (IsDisposed) + { + throw new ObjectDisposedException(nameof(Disposer), DisposerResources.CannotAddToDisposedDisposer); + } + + _synchRoot.Wait(); + try + { _items.Push(instance); + } + finally + { + _synchRoot.Release(); + } } } } diff --git a/src/Autofac/Core/DisposerResources.Designer.cs b/src/Autofac/Core/DisposerResources.Designer.cs new file mode 100644 index 000000000..c6ed5fb32 --- /dev/null +++ b/src/Autofac/Core/DisposerResources.Designer.cs @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Autofac.Core { + using System; + using System.Reflection; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DisposerResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DisposerResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Autofac.Core.DisposerResources", typeof(DisposerResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The Disposer object has already been Disposed, so no items can be added to it.. + /// + internal static string CannotAddToDisposedDisposer { + get { + return ResourceManager.GetString("CannotAddToDisposedDisposer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A synchronous Dispose has been attempted, but the tracked object of type '{0}' only implements IAsyncDisposable.. + /// + internal static string TypeOnlyImplementsIAsyncDisposable { + get { + return ResourceManager.GetString("TypeOnlyImplementsIAsyncDisposable", resourceCulture); + } + } + } +} diff --git a/src/Autofac/Core/DisposerResources.resx b/src/Autofac/Core/DisposerResources.resx new file mode 100644 index 000000000..2ec5f5fbc --- /dev/null +++ b/src/Autofac/Core/DisposerResources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The Disposer object has already been Disposed, so no items can be added to it. + + + A synchronous Dispose has been attempted, but the tracked object of type '{0}' only implements IAsyncDisposable. + + \ No newline at end of file diff --git a/src/Autofac/Core/IDisposer.cs b/src/Autofac/Core/IDisposer.cs index f37ce7939..da023ccc7 100644 --- a/src/Autofac/Core/IDisposer.cs +++ b/src/Autofac/Core/IDisposer.cs @@ -31,7 +31,11 @@ namespace Autofac.Core /// Provided on an object that will dispose of other objects when it is /// itself disposed. /// +#if NETCOREAPP3_0 + public interface IDisposer : IDisposable, IAsyncDisposable +#else public interface IDisposer : IDisposable +#endif { /// /// Adds an object to the disposer. When the disposer is @@ -39,5 +43,18 @@ public interface IDisposer : IDisposable /// /// The instance. void AddInstanceForDisposal(IDisposable instance); +#if NETCOREAPP3_0 + /// + /// Adds an object to the disposer, where that object implements IAsyncDisposable. When the disposer is + /// disposed, so will the object be. + /// You should most likely implement IDisposable as well, and call instead of this method. + /// + /// The instance. + /// + /// If the provided object only implements IAsyncDisposable, and the is disposed of using a synchronous Dispose call, + /// that call will throw an exception when it attempts to dispose of the provided instance. + /// + void AddInstanceForAsyncDisposal(IAsyncDisposable instance); +#endif } } diff --git a/src/Autofac/Core/Resolving/InstanceLookup.cs b/src/Autofac/Core/Resolving/InstanceLookup.cs index 040abb376..46e4689ad 100644 --- a/src/Autofac/Core/Resolving/InstanceLookup.cs +++ b/src/Autofac/Core/Resolving/InstanceLookup.cs @@ -144,7 +144,15 @@ private object Activate(IEnumerable parameters, out object decoratorT // instance once the instance has been activated - assuming that it will be // done during the lifetime scope's Disposer executing. if (decoratorTarget is IDisposable instanceAsDisposable) + { _activationScope.Disposer.AddInstanceForDisposal(instanceAsDisposable); + } +#if NETCOREAPP3_0 + else if (decoratorTarget is IAsyncDisposable asyncDisposableInstance) + { + _activationScope.Disposer.AddInstanceForAsyncDisposal(asyncDisposableInstance); + } +#endif } ComponentRegistration.RaiseActivating(this, resolveParameters, ref _newInstance); diff --git a/src/Autofac/Core/ServiceResources.Designer.cs b/src/Autofac/Core/ServiceResources.Designer.cs index cecee5589..52e883b3d 100644 --- a/src/Autofac/Core/ServiceResources.Designer.cs +++ b/src/Autofac/Core/ServiceResources.Designer.cs @@ -11,8 +11,6 @@ namespace Autofac.Core { using System; using System.Reflection; - - /// /// A strongly-typed resource class, for looking up localized strings, etc. /// diff --git a/src/Autofac/Util/Disposable.cs b/src/Autofac/Util/Disposable.cs index d5a406009..27674c595 100644 --- a/src/Autofac/Util/Disposable.cs +++ b/src/Autofac/Util/Disposable.cs @@ -26,13 +26,18 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading; +using System.Threading.Tasks; namespace Autofac.Util { /// /// Base class for disposable objects. /// +#if NETCOREAPP3_0 + public class Disposable : IDisposable, IAsyncDisposable +#else public class Disposable : IDisposable +#endif { private const int DisposedFlag = 1; private int _isDisposed; @@ -70,5 +75,37 @@ protected bool IsDisposed return _isDisposed == DisposedFlag; } } + +#if NETCOREAPP3_0 + + [SuppressMessage( + "Usage", + "CA1816:Dispose methods should call SuppressFinalize", + Justification = "DisposeAsync should also call SuppressFinalize (see various .NET internal implementations).")] + public ValueTask DisposeAsync() + { + // Still need to check if we've already disposed; can't do both. + var wasDisposed = Interlocked.Exchange(ref _isDisposed, DisposedFlag); + if (wasDisposed != DisposedFlag) + { + GC.SuppressFinalize(this); + + // Always true, but means we get the similar syntax as Dispose, + // and separates the two overloads. + return DisposeAsync(true); + } + + return default(ValueTask); + } + + protected virtual ValueTask DisposeAsync(bool disposing) + { + // Default implementation does a synchronous dispose. + Dispose(disposing); + + return default; + } + +#endif } } diff --git a/test/Autofac.Test/Core/DisposerTests.cs b/test/Autofac.Test/Core/DisposerTests.cs index 0bd09ace7..7c3188732 100644 --- a/test/Autofac.Test/Core/DisposerTests.cs +++ b/test/Autofac.Test/Core/DisposerTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Autofac.Core; using Autofac.Test.Util; @@ -38,5 +39,137 @@ public void OnDispose_DisposerDisposesContainedInstances() disposer.Dispose(); Assert.True(instance.IsDisposed); } + + [Fact] + public void CannotAddObjectsToDisposerAfterSyncDispose() + { + var instance = new DisposeTracker(); + + var disposer = new Disposer(); + disposer.AddInstanceForDisposal(instance); + Assert.False(instance.IsDisposed); + Assert.False(instance.IsDisposed); + disposer.Dispose(); + Assert.True(instance.IsDisposed); + + Assert.Throws(() => + { + disposer.AddInstanceForDisposal(instance); + }); + } + +#if NETCOREAPP3_0 + [Fact] + public void DisposerDisposesOfObjectsAsyncIfIAsyncDisposableDeclared() + { + var instance = new AsyncDisposeTracker(); + + var disposer = new Disposer(); + disposer.AddInstanceForDisposal(instance); + Assert.False(instance.IsSyncDisposed); + Assert.False(instance.IsAsyncDisposed); + var result = disposer.DisposeAsync(); + Assert.False(instance.IsSyncDisposed); + + // Dispose is happening async, so this won't be true yet. + Assert.False(instance.IsAsyncDisposed); + + // Now we wait. + result.GetAwaiter().GetResult(); + + Assert.False(instance.IsSyncDisposed); + Assert.True(instance.IsAsyncDisposed); + } + + [Fact] + public void DisposerDisposesOfObjectsSyncIfIDisposableOnly() + { + var instance = new DisposeTracker(); + + var disposer = new Disposer(); + disposer.AddInstanceForDisposal(instance); + Assert.False(instance.IsDisposed); + disposer.DisposeAsync().GetAwaiter().GetResult(); + Assert.True(instance.IsDisposed); + } + + [Fact] + public void DisposerDisposesOfObjectsSyncIfIAsyncDisposableDeclaredButSyncDisposeCalled() + { + var instance = new AsyncDisposeTracker(); + + var disposer = new Disposer(); + disposer.AddInstanceForDisposal(instance); + Assert.False(instance.IsSyncDisposed); + Assert.False(instance.IsAsyncDisposed); + disposer.Dispose(); + Assert.True(instance.IsSyncDisposed); + Assert.False(instance.IsAsyncDisposed); + } + + [Fact] + public void CannotAddObjectsToDisposerAfterAsyncDispose() + { + var instance = new AsyncDisposeTracker(); + + var disposer = new Disposer(); + disposer.AddInstanceForDisposal(instance); + Assert.False(instance.IsSyncDisposed); + Assert.False(instance.IsAsyncDisposed); + disposer.DisposeAsync().GetAwaiter().GetResult(); + Assert.False(instance.IsSyncDisposed); + Assert.True(instance.IsAsyncDisposed); + + Assert.Throws(() => + { + disposer.AddInstanceForDisposal(instance); + }); + } + + [Fact] + public void SyncDisposalOnObjectWithNoIDisposableThrows() + { + var instance = new AsyncOnlyDisposeTracker(); + + var disposer = new Disposer(); + disposer.AddInstanceForAsyncDisposal(instance); + + Assert.Throws(() => + { + disposer.Dispose(); + }); + } + + [Fact] + public void DisposerAsyncDisposesContainedInstances_InReverseOfOrderAdded() + { + var disposeOrder = new List(); + + var asyncInstance1 = new AsyncDisposeTracker(); + asyncInstance1.Disposing += (s, e) => disposeOrder.Add(asyncInstance1); + var asyncOnlyInstance2 = new AsyncOnlyDisposeTracker(); + asyncOnlyInstance2.Disposing += (s, e) => disposeOrder.Add(asyncOnlyInstance2); + var syncInstance3 = new DisposeTracker(); + syncInstance3.Disposing += (s, e) => disposeOrder.Add(syncInstance3); + var syncInstance4 = new DisposeTracker(); + syncInstance4.Disposing += (s, e) => disposeOrder.Add(syncInstance4); + + var disposer = new Disposer(); + + disposer.AddInstanceForDisposal(asyncInstance1); + disposer.AddInstanceForDisposal(syncInstance3); + disposer.AddInstanceForDisposal(syncInstance4); + disposer.AddInstanceForAsyncDisposal(asyncOnlyInstance2); + + disposer.DisposeAsync().GetAwaiter().GetResult(); + + Assert.Collection( + disposeOrder, + o1 => Assert.Same(asyncOnlyInstance2, o1), + o2 => Assert.Same(syncInstance4, o2), + o3 => Assert.Same(syncInstance3, o3), + o4 => Assert.Same(asyncInstance1, o4)); + } +#endif } } diff --git a/test/Autofac.Test/Util/AsyncDisposeTracker.cs b/test/Autofac.Test/Util/AsyncDisposeTracker.cs new file mode 100644 index 000000000..5b6a32963 --- /dev/null +++ b/test/Autofac.Test/Util/AsyncDisposeTracker.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; + +namespace Autofac.Test.Util +{ +#if NETCOREAPP3_0 + public class AsyncDisposeTracker : IDisposable, IAsyncDisposable + { + public event EventHandler Disposing; + + public bool IsSyncDisposed { get; set; } + + public bool IsAsyncDisposed { get; set; } + + public void Dispose() + { + this.IsSyncDisposed = true; + + if (this.Disposing != null) + { + this.Disposing(this, EventArgs.Empty); + } + } + + public async ValueTask DisposeAsync() + { + await Task.Delay(1); + + IsAsyncDisposed = true; + + if (this.Disposing != null) + { + this.Disposing(this, EventArgs.Empty); + } + } + } +#endif +} diff --git a/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs b/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs new file mode 100644 index 000000000..eb5894e0e --- /dev/null +++ b/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; + +namespace Autofac.Test.Util +{ +#if NETCOREAPP3_0 + public class AsyncOnlyDisposeTracker : IAsyncDisposable + { + public event EventHandler Disposing; + + public bool IsAsyncDisposed { get; set; } + + public async ValueTask DisposeAsync() + { + await Task.Delay(1); + + IsAsyncDisposed = true; + + if (this.Disposing != null) + { + this.Disposing(this, EventArgs.Empty); + } + } + } +#endif +} diff --git a/test/Autofac.Test/Util/DisposeTracker.cs b/test/Autofac.Test/Util/DisposeTracker.cs index 8252e8d06..7df56cd8c 100644 --- a/test/Autofac.Test/Util/DisposeTracker.cs +++ b/test/Autofac.Test/Util/DisposeTracker.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; namespace Autofac.Test.Util { From 475d002bfb5d5b5b676ae9b30a8a995904ef852c Mon Sep 17 00:00:00 2001 From: alistairjevans Date: Fri, 18 Oct 2019 23:20:03 +0100 Subject: [PATCH 2/8] Updated LifetimeScope and Container to support AsyncDisposal; added tests to verify that the async dispose follows down onto the individual services. --- src/Autofac/Core/Container.cs | 17 +++++ src/Autofac/Core/Lifetime/LifetimeScope.cs | 25 +++++++ src/Autofac/ILifetimeScope.cs | 4 ++ test/Autofac.Test/Core/ContainerTests.cs | 24 +++++++ .../Core/Lifetime/LifetimeScopeTests.cs | 65 +++++++++++++++++++ 5 files changed, 135 insertions(+) diff --git a/src/Autofac/Core/Container.cs b/src/Autofac/Core/Container.cs index 3512832ef..3f93a71c7 100644 --- a/src/Autofac/Core/Container.cs +++ b/src/Autofac/Core/Container.cs @@ -27,6 +27,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; using Autofac.Core.Activators.Delegate; using Autofac.Core.Lifetime; using Autofac.Core.Registration; @@ -178,6 +179,22 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } +#if NETCOREAPP3_0 + protected override async ValueTask DisposeAsync(bool disposing) + { + if (disposing) + { + await _rootLifetimeScope.DisposeAsync(); + + // Registries are not likely to have async tasks to dispose of, + // so we will leave it as a straight dispose. + ComponentRegistry.Dispose(); + } + + // Do not call the base, otherwise the standard Dispose will fire. + } +#endif + /// /// Gets the service object of the specified type. /// diff --git a/src/Autofac/Core/Lifetime/LifetimeScope.cs b/src/Autofac/Core/Lifetime/LifetimeScope.cs index 8541a7bd4..b9d0c322e 100644 --- a/src/Autofac/Core/Lifetime/LifetimeScope.cs +++ b/src/Autofac/Core/Lifetime/LifetimeScope.cs @@ -29,6 +29,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using Autofac.Builder; using Autofac.Core.Registration; using Autofac.Core.Resolving; @@ -357,6 +358,30 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } +#if NETCOREAPP3_0 + protected override async ValueTask DisposeAsync(bool disposing) + { + if (disposing) + { + var handler = CurrentScopeEnding; + + try + { + handler?.Invoke(this, new LifetimeScopeEndingEventArgs(this)); + } + finally + { + await Disposer.DisposeAsync(); + } + + // ReSharper disable once InconsistentlySynchronizedField + _sharedInstances.Clear(); + } + + // Don't call the base (which would just call the normal Dispose). + } +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CheckNotDisposed() { diff --git a/src/Autofac/ILifetimeScope.cs b/src/Autofac/ILifetimeScope.cs index 434be5a53..21361544f 100644 --- a/src/Autofac/ILifetimeScope.cs +++ b/src/Autofac/ILifetimeScope.cs @@ -69,7 +69,11 @@ namespace Autofac /// /// /// +#if NETCOREAPP3_0 + public interface ILifetimeScope : IComponentContext, IDisposable, IAsyncDisposable +#else public interface ILifetimeScope : IComponentContext, IDisposable +#endif { /// /// Begin a new nested scope. Component instances created via the new scope diff --git a/test/Autofac.Test/Core/ContainerTests.cs b/test/Autofac.Test/Core/ContainerTests.cs index 3abdef000..0de3f07b3 100644 --- a/test/Autofac.Test/Core/ContainerTests.cs +++ b/test/Autofac.Test/Core/ContainerTests.cs @@ -1,6 +1,8 @@ using System; +using System.Threading.Tasks; using Autofac.Core; using Autofac.Test.Scenarios.Parameterisation; +using Autofac.Test.Util; using Xunit; namespace Autofac.Test.Core @@ -177,6 +179,28 @@ public void ReplaceInstance_ModuleActivatingHandlerProvidesResultToRelease() } } +#if NETCOREAPP3_0 + [Fact] + public async ValueTask AsyncContainerDisposeTriggersAsyncServiceDispose() + { + var builder = new ContainerBuilder(); + builder.Register(c => new AsyncDisposeTracker()).SingleInstance(); + + AsyncDisposeTracker tracker; + + await using (var container = builder.Build()) + { + tracker = container.Resolve(); + + Assert.False(tracker.IsSyncDisposed); + Assert.False(tracker.IsAsyncDisposed); + } + + Assert.False(tracker.IsSyncDisposed); + Assert.True(tracker.IsAsyncDisposed); + } +#endif + private class ReplaceInstanceModule : Module { protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) diff --git a/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs b/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs index 8d4a580f4..c852a28b8 100644 --- a/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs +++ b/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs @@ -1,8 +1,10 @@ using System; using System.Linq; +using System.Threading.Tasks; using Autofac.Core; using Autofac.Core.Registration; using Autofac.Test.Scenarios.RegistrationSources; +using Autofac.Test.Util; using Xunit; namespace Autofac.Test.Core.Lifetime @@ -100,6 +102,69 @@ public void NestedLifetimeScopesMaintainServiceLimitTypes() } } +#if NETCOREAPP3_0 + [Fact] + public async ValueTask AsyncDisposeLifetimeScopeDisposesRegistrationsAsync() + { + var cb = new ContainerBuilder(); + + cb.RegisterType().InstancePerLifetimeScope().AsSelf(); + cb.RegisterType().InstancePerLifetimeScope().AsSelf(); + cb.RegisterType().InstancePerLifetimeScope().AsSelf(); + + var container = cb.Build(); + + DisposeTracker tracker; + AsyncDisposeTracker asyncTracker; + AsyncOnlyDisposeTracker asyncOnlyTracker; + + await using (var scope = container.BeginLifetimeScope()) + { + tracker = scope.Resolve(); + asyncTracker = scope.Resolve(); + asyncOnlyTracker = scope.Resolve(); + + Assert.False(tracker.IsDisposed); + Assert.False(asyncTracker.IsSyncDisposed); + Assert.False(asyncTracker.IsAsyncDisposed); + Assert.False(asyncOnlyTracker.IsAsyncDisposed); + } + + Assert.True(tracker.IsDisposed); + Assert.True(asyncTracker.IsAsyncDisposed); + Assert.True(asyncOnlyTracker.IsAsyncDisposed); + Assert.False(asyncTracker.IsSyncDisposed); + } + + [Fact] + public void DisposeLifetimeScopeDisposesRegistrationsThatAreAsyncAndSyncDispose() + { + var cb = new ContainerBuilder(); + + cb.RegisterType().InstancePerLifetimeScope().AsSelf(); + cb.RegisterType().InstancePerLifetimeScope().AsSelf(); + + var container = cb.Build(); + + DisposeTracker tracker; + AsyncDisposeTracker asyncTracker; + + using (var scope = container.BeginLifetimeScope()) + { + tracker = scope.Resolve(); + asyncTracker = scope.Resolve(); + + Assert.False(tracker.IsDisposed); + Assert.False(asyncTracker.IsSyncDisposed); + Assert.False(asyncTracker.IsAsyncDisposed); + } + + Assert.True(tracker.IsDisposed); + Assert.False(asyncTracker.IsAsyncDisposed); + Assert.True(asyncTracker.IsSyncDisposed); + } +#endif + internal class DependsOnRegisteredInstance { public DependsOnRegisteredInstance(object instance) From 46d8a076eb3eaaa105aadbb3c1be63bcc4ac22a4 Mon Sep 17 00:00:00 2001 From: alistairjevans Date: Fri, 18 Oct 2019 23:54:26 +0100 Subject: [PATCH 3/8] Reverted accidental changes --- src/Autofac/Core/ServiceResources.Designer.cs | 2 ++ test/Autofac.Test/Util/DisposeTracker.cs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Autofac/Core/ServiceResources.Designer.cs b/src/Autofac/Core/ServiceResources.Designer.cs index 52e883b3d..62362a2dc 100644 --- a/src/Autofac/Core/ServiceResources.Designer.cs +++ b/src/Autofac/Core/ServiceResources.Designer.cs @@ -11,6 +11,8 @@ namespace Autofac.Core { using System; using System.Reflection; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// diff --git a/test/Autofac.Test/Util/DisposeTracker.cs b/test/Autofac.Test/Util/DisposeTracker.cs index 7df56cd8c..8252e8d06 100644 --- a/test/Autofac.Test/Util/DisposeTracker.cs +++ b/test/Autofac.Test/Util/DisposeTracker.cs @@ -1,5 +1,4 @@ using System; -using System.Threading.Tasks; namespace Autofac.Test.Util { From 04ad07046a8d660a8d937935954dc820d0ffd49e Mon Sep 17 00:00:00 2001 From: alistairjevans Date: Sat, 19 Oct 2019 11:02:36 +0100 Subject: [PATCH 4/8] Changed Autofac to target netstandard2.1 rather than netcoreapp3.0. Added conditional package reference for netstandard2.0. --- src/Autofac/Autofac.csproj | 10 +++++++++- src/Autofac/Core/Container.cs | 2 +- src/Autofac/Core/Disposer.cs | 2 +- src/Autofac/Core/IDisposer.cs | 4 ++-- src/Autofac/Core/Lifetime/LifetimeScope.cs | 2 +- src/Autofac/Core/Resolving/InstanceLookup.cs | 2 +- src/Autofac/ILifetimeScope.cs | 2 +- src/Autofac/Util/Disposable.cs | 4 ++-- .../Autofac.Specification.Test.csproj | 4 ++++ test/Autofac.Test/Autofac.Test.csproj | 4 ++++ test/Autofac.Test/Core/ContainerTests.cs | 2 +- test/Autofac.Test/Core/DisposerTests.cs | 2 +- test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs | 2 +- test/Autofac.Test/Util/AsyncDisposeTracker.cs | 2 +- test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs | 2 +- 15 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/Autofac/Autofac.csproj b/src/Autofac/Autofac.csproj index 4232b77fc..5979dcc7a 100644 --- a/src/Autofac/Autofac.csproj +++ b/src/Autofac/Autofac.csproj @@ -3,7 +3,7 @@ Autofac is an IoC container for Microsoft .NET. It manages the dependencies between classes so that applications stay easy to change as they grow in size and complexity. 5.0.0 - netcoreapp3.0;netstandard2.0;netstandard1.1;net45 + netstandard2.1;netstandard2.0;netstandard1.1;net45 latest $(NoWarn);CS1591;IDE0008 true @@ -35,6 +35,10 @@ Autofac + + ASYNC_DISPOSE_AVAILABLE;$(DefineConstants) + + All @@ -54,6 +58,10 @@ + + + + True diff --git a/src/Autofac/Core/Container.cs b/src/Autofac/Core/Container.cs index 3f93a71c7..b4eced3e7 100644 --- a/src/Autofac/Core/Container.cs +++ b/src/Autofac/Core/Container.cs @@ -179,7 +179,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE protected override async ValueTask DisposeAsync(bool disposing) { if (disposing) diff --git a/src/Autofac/Core/Disposer.cs b/src/Autofac/Core/Disposer.cs index 5b584d296..8f6107d11 100644 --- a/src/Autofac/Core/Disposer.cs +++ b/src/Autofac/Core/Disposer.cs @@ -90,7 +90,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE protected override async ValueTask DisposeAsync(bool disposing) { if (disposing) diff --git a/src/Autofac/Core/IDisposer.cs b/src/Autofac/Core/IDisposer.cs index da023ccc7..588c320a4 100644 --- a/src/Autofac/Core/IDisposer.cs +++ b/src/Autofac/Core/IDisposer.cs @@ -31,7 +31,7 @@ namespace Autofac.Core /// Provided on an object that will dispose of other objects when it is /// itself disposed. /// -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE public interface IDisposer : IDisposable, IAsyncDisposable #else public interface IDisposer : IDisposable @@ -43,7 +43,7 @@ public interface IDisposer : IDisposable /// /// The instance. void AddInstanceForDisposal(IDisposable instance); -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE /// /// Adds an object to the disposer, where that object implements IAsyncDisposable. When the disposer is /// disposed, so will the object be. diff --git a/src/Autofac/Core/Lifetime/LifetimeScope.cs b/src/Autofac/Core/Lifetime/LifetimeScope.cs index b9d0c322e..f5215541a 100644 --- a/src/Autofac/Core/Lifetime/LifetimeScope.cs +++ b/src/Autofac/Core/Lifetime/LifetimeScope.cs @@ -358,7 +358,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE protected override async ValueTask DisposeAsync(bool disposing) { if (disposing) diff --git a/src/Autofac/Core/Resolving/InstanceLookup.cs b/src/Autofac/Core/Resolving/InstanceLookup.cs index 46e4689ad..81cbe6d26 100644 --- a/src/Autofac/Core/Resolving/InstanceLookup.cs +++ b/src/Autofac/Core/Resolving/InstanceLookup.cs @@ -147,7 +147,7 @@ private object Activate(IEnumerable parameters, out object decoratorT { _activationScope.Disposer.AddInstanceForDisposal(instanceAsDisposable); } -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE else if (decoratorTarget is IAsyncDisposable asyncDisposableInstance) { _activationScope.Disposer.AddInstanceForAsyncDisposal(asyncDisposableInstance); diff --git a/src/Autofac/ILifetimeScope.cs b/src/Autofac/ILifetimeScope.cs index 21361544f..4fe1ac06c 100644 --- a/src/Autofac/ILifetimeScope.cs +++ b/src/Autofac/ILifetimeScope.cs @@ -69,7 +69,7 @@ namespace Autofac /// /// /// -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE public interface ILifetimeScope : IComponentContext, IDisposable, IAsyncDisposable #else public interface ILifetimeScope : IComponentContext, IDisposable diff --git a/src/Autofac/Util/Disposable.cs b/src/Autofac/Util/Disposable.cs index 27674c595..f5ea1c72e 100644 --- a/src/Autofac/Util/Disposable.cs +++ b/src/Autofac/Util/Disposable.cs @@ -33,7 +33,7 @@ namespace Autofac.Util /// /// Base class for disposable objects. /// -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE public class Disposable : IDisposable, IAsyncDisposable #else public class Disposable : IDisposable @@ -76,7 +76,7 @@ protected bool IsDisposed } } -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE [SuppressMessage( "Usage", diff --git a/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj b/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj index f3ee91d5d..24f096adc 100644 --- a/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj +++ b/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj @@ -19,6 +19,10 @@ + + ASYNC_DISPOSE_AVAILABLE;$(DefineConstants) + + diff --git a/test/Autofac.Test/Autofac.Test.csproj b/test/Autofac.Test/Autofac.Test.csproj index 4fa726766..b7b076403 100644 --- a/test/Autofac.Test/Autofac.Test.csproj +++ b/test/Autofac.Test/Autofac.Test.csproj @@ -39,6 +39,10 @@ $(DefineConstants);PARTIAL_TRUST + + ASYNC_DISPOSE_AVAILABLE;$(DefineConstants) + + diff --git a/test/Autofac.Test/Core/ContainerTests.cs b/test/Autofac.Test/Core/ContainerTests.cs index 0de3f07b3..de9ebf533 100644 --- a/test/Autofac.Test/Core/ContainerTests.cs +++ b/test/Autofac.Test/Core/ContainerTests.cs @@ -179,7 +179,7 @@ public void ReplaceInstance_ModuleActivatingHandlerProvidesResultToRelease() } } -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE [Fact] public async ValueTask AsyncContainerDisposeTriggersAsyncServiceDispose() { diff --git a/test/Autofac.Test/Core/DisposerTests.cs b/test/Autofac.Test/Core/DisposerTests.cs index 7c3188732..d4d433ce5 100644 --- a/test/Autofac.Test/Core/DisposerTests.cs +++ b/test/Autofac.Test/Core/DisposerTests.cs @@ -58,7 +58,7 @@ public void CannotAddObjectsToDisposerAfterSyncDispose() }); } -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE [Fact] public void DisposerDisposesOfObjectsAsyncIfIAsyncDisposableDeclared() { diff --git a/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs b/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs index c852a28b8..bf511c867 100644 --- a/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs +++ b/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs @@ -102,7 +102,7 @@ public void NestedLifetimeScopesMaintainServiceLimitTypes() } } -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE [Fact] public async ValueTask AsyncDisposeLifetimeScopeDisposesRegistrationsAsync() { diff --git a/test/Autofac.Test/Util/AsyncDisposeTracker.cs b/test/Autofac.Test/Util/AsyncDisposeTracker.cs index 5b6a32963..2b9e21cd4 100644 --- a/test/Autofac.Test/Util/AsyncDisposeTracker.cs +++ b/test/Autofac.Test/Util/AsyncDisposeTracker.cs @@ -3,7 +3,7 @@ namespace Autofac.Test.Util { -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE public class AsyncDisposeTracker : IDisposable, IAsyncDisposable { public event EventHandler Disposing; diff --git a/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs b/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs index eb5894e0e..605d5b568 100644 --- a/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs +++ b/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs @@ -3,7 +3,7 @@ namespace Autofac.Test.Util { -#if NETCOREAPP3_0 +#if ASYNC_DISPOSE_AVAILABLE public class AsyncOnlyDisposeTracker : IAsyncDisposable { public event EventHandler Disposing; From 16caf1a27d6f0a7f70ead9506332fa6dba8181e8 Mon Sep 17 00:00:00 2001 From: alistairjevans Date: Sat, 19 Oct 2019 11:28:11 +0100 Subject: [PATCH 5/8] Correct comments in Disposer --- src/Autofac/Core/Disposer.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Autofac/Core/Disposer.cs b/src/Autofac/Core/Disposer.cs index 8f6107d11..4a9837a83 100644 --- a/src/Autofac/Core/Disposer.cs +++ b/src/Autofac/Core/Disposer.cs @@ -43,7 +43,7 @@ internal class Disposer : Disposable, IDisposer private Stack _items = new Stack(); // Need to use a semaphore instead of a simple object to lock on, because - // we need to synchronoise an awaitable block. + // we need to synchronise an awaitable block. private SemaphoreSlim _synchRoot = new SemaphoreSlim(1, 1); /// @@ -61,14 +61,16 @@ protected override void Dispose(bool disposing) { var item = _items.Pop(); - // If we are in synchronous dispose, and an object doesn't implement + // If we are in synchronous dispose, and an object implements IDisposable, + // then use it. if (item is IDisposable disposable) { disposable.Dispose(); } else { - // Type only implements IAsyncDisposable, which is not valid if there is a synchronous dispose being done. + // Type only implements IAsyncDisposable, which is not valid if there + // is a synchronous dispose being done. throw new InvalidOperationException(string.Format( DisposerResources.Culture, DisposerResources.TypeOnlyImplementsIAsyncDisposable, From c8e515f419788639345816ec9adc14a7ec04c5f2 Mon Sep 17 00:00:00 2001 From: alistairjevans Date: Sun, 20 Oct 2019 10:27:56 +0100 Subject: [PATCH 6/8] Dropped support for NET Framework 4.5 and NET Standard 1.1. Removed #if blocks for async dispose support. --- src/Autofac/Autofac.csproj | 10 +--------- src/Autofac/Core/Container.cs | 2 -- src/Autofac/Core/Disposer.cs | 2 -- src/Autofac/Core/IDisposer.cs | 7 +------ src/Autofac/Core/Lifetime/LifetimeScope.cs | 2 -- src/Autofac/Core/Resolving/InstanceLookup.cs | 2 -- src/Autofac/ILifetimeScope.cs | 4 ---- src/Autofac/Util/Disposable.cs | 8 -------- .../Autofac.Specification.Test.csproj | 7 ++----- .../Autofac.Test.Scenarios.ScannedAssembly.csproj | 3 +-- test/Autofac.Test/Autofac.Test.csproj | 13 +++++-------- test/Autofac.Test/Core/ContainerTests.cs | 2 -- test/Autofac.Test/Core/DisposerTests.cs | 2 -- .../Core/Lifetime/LifetimeScopeTests.cs | 2 -- test/Autofac.Test/Util/AsyncDisposeTracker.cs | 2 -- test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs | 2 -- 16 files changed, 10 insertions(+), 60 deletions(-) diff --git a/src/Autofac/Autofac.csproj b/src/Autofac/Autofac.csproj index 5979dcc7a..d4ec74147 100644 --- a/src/Autofac/Autofac.csproj +++ b/src/Autofac/Autofac.csproj @@ -3,7 +3,7 @@ Autofac is an IoC container for Microsoft .NET. It manages the dependencies between classes so that applications stay easy to change as they grow in size and complexity. 5.0.0 - netstandard2.1;netstandard2.0;netstandard1.1;net45 + netstandard2.1;netstandard2.0 latest $(NoWarn);CS1591;IDE0008 true @@ -35,10 +35,6 @@ Autofac - - ASYNC_DISPOSE_AVAILABLE;$(DefineConstants) - - All @@ -54,10 +50,6 @@ - - - - diff --git a/src/Autofac/Core/Container.cs b/src/Autofac/Core/Container.cs index b4eced3e7..1285faa7c 100644 --- a/src/Autofac/Core/Container.cs +++ b/src/Autofac/Core/Container.cs @@ -179,7 +179,6 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } -#if ASYNC_DISPOSE_AVAILABLE protected override async ValueTask DisposeAsync(bool disposing) { if (disposing) @@ -193,7 +192,6 @@ protected override async ValueTask DisposeAsync(bool disposing) // Do not call the base, otherwise the standard Dispose will fire. } -#endif /// /// Gets the service object of the specified type. diff --git a/src/Autofac/Core/Disposer.cs b/src/Autofac/Core/Disposer.cs index 4a9837a83..03da9621b 100644 --- a/src/Autofac/Core/Disposer.cs +++ b/src/Autofac/Core/Disposer.cs @@ -92,7 +92,6 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } -#if ASYNC_DISPOSE_AVAILABLE protected override async ValueTask DisposeAsync(bool disposing) { if (disposing) @@ -148,7 +147,6 @@ public void AddInstanceForAsyncDisposal(IAsyncDisposable instance) { AddInternal(instance); } -#endif /// /// Adds an object to the disposer. When the disposer is diff --git a/src/Autofac/Core/IDisposer.cs b/src/Autofac/Core/IDisposer.cs index 588c320a4..9e21c99f7 100644 --- a/src/Autofac/Core/IDisposer.cs +++ b/src/Autofac/Core/IDisposer.cs @@ -31,11 +31,7 @@ namespace Autofac.Core /// Provided on an object that will dispose of other objects when it is /// itself disposed. /// -#if ASYNC_DISPOSE_AVAILABLE public interface IDisposer : IDisposable, IAsyncDisposable -#else - public interface IDisposer : IDisposable -#endif { /// /// Adds an object to the disposer. When the disposer is @@ -43,7 +39,7 @@ public interface IDisposer : IDisposable /// /// The instance. void AddInstanceForDisposal(IDisposable instance); -#if ASYNC_DISPOSE_AVAILABLE + /// /// Adds an object to the disposer, where that object implements IAsyncDisposable. When the disposer is /// disposed, so will the object be. @@ -55,6 +51,5 @@ public interface IDisposer : IDisposable /// that call will throw an exception when it attempts to dispose of the provided instance. /// void AddInstanceForAsyncDisposal(IAsyncDisposable instance); -#endif } } diff --git a/src/Autofac/Core/Lifetime/LifetimeScope.cs b/src/Autofac/Core/Lifetime/LifetimeScope.cs index f5215541a..901021bce 100644 --- a/src/Autofac/Core/Lifetime/LifetimeScope.cs +++ b/src/Autofac/Core/Lifetime/LifetimeScope.cs @@ -358,7 +358,6 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } -#if ASYNC_DISPOSE_AVAILABLE protected override async ValueTask DisposeAsync(bool disposing) { if (disposing) @@ -380,7 +379,6 @@ protected override async ValueTask DisposeAsync(bool disposing) // Don't call the base (which would just call the normal Dispose). } -#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CheckNotDisposed() diff --git a/src/Autofac/Core/Resolving/InstanceLookup.cs b/src/Autofac/Core/Resolving/InstanceLookup.cs index 81cbe6d26..3650e8e25 100644 --- a/src/Autofac/Core/Resolving/InstanceLookup.cs +++ b/src/Autofac/Core/Resolving/InstanceLookup.cs @@ -147,12 +147,10 @@ private object Activate(IEnumerable parameters, out object decoratorT { _activationScope.Disposer.AddInstanceForDisposal(instanceAsDisposable); } -#if ASYNC_DISPOSE_AVAILABLE else if (decoratorTarget is IAsyncDisposable asyncDisposableInstance) { _activationScope.Disposer.AddInstanceForAsyncDisposal(asyncDisposableInstance); } -#endif } ComponentRegistration.RaiseActivating(this, resolveParameters, ref _newInstance); diff --git a/src/Autofac/ILifetimeScope.cs b/src/Autofac/ILifetimeScope.cs index 4fe1ac06c..a1d5d6607 100644 --- a/src/Autofac/ILifetimeScope.cs +++ b/src/Autofac/ILifetimeScope.cs @@ -69,11 +69,7 @@ namespace Autofac /// /// /// -#if ASYNC_DISPOSE_AVAILABLE public interface ILifetimeScope : IComponentContext, IDisposable, IAsyncDisposable -#else - public interface ILifetimeScope : IComponentContext, IDisposable -#endif { /// /// Begin a new nested scope. Component instances created via the new scope diff --git a/src/Autofac/Util/Disposable.cs b/src/Autofac/Util/Disposable.cs index f5ea1c72e..1f38c0350 100644 --- a/src/Autofac/Util/Disposable.cs +++ b/src/Autofac/Util/Disposable.cs @@ -33,11 +33,7 @@ namespace Autofac.Util /// /// Base class for disposable objects. /// -#if ASYNC_DISPOSE_AVAILABLE public class Disposable : IDisposable, IAsyncDisposable -#else - public class Disposable : IDisposable -#endif { private const int DisposedFlag = 1; private int _isDisposed; @@ -76,8 +72,6 @@ protected bool IsDisposed } } -#if ASYNC_DISPOSE_AVAILABLE - [SuppressMessage( "Usage", "CA1816:Dispose methods should call SuppressFinalize", @@ -105,7 +99,5 @@ protected virtual ValueTask DisposeAsync(bool disposing) return default; } - -#endif } } diff --git a/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj b/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj index 24f096adc..b69eea8c9 100644 --- a/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj +++ b/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj @@ -1,11 +1,12 @@  - netcoreapp3.0;net46 + netcoreapp3.0;net472 $(NoWarn);CS1591;SA1602;SA1611 true ../../build/Analyzers.ruleset false + latest @@ -18,10 +19,6 @@ - - - ASYNC_DISPOSE_AVAILABLE;$(DefineConstants) - diff --git a/test/Autofac.Test.Scenarios.ScannedAssembly/Autofac.Test.Scenarios.ScannedAssembly.csproj b/test/Autofac.Test.Scenarios.ScannedAssembly/Autofac.Test.Scenarios.ScannedAssembly.csproj index 98e68cd28..e63da0e20 100644 --- a/test/Autofac.Test.Scenarios.ScannedAssembly/Autofac.Test.Scenarios.ScannedAssembly.csproj +++ b/test/Autofac.Test.Scenarios.ScannedAssembly/Autofac.Test.Scenarios.ScannedAssembly.csproj @@ -1,7 +1,7 @@  - net45;netstandard1.1;netstandard2.0 + netstandard2.0 $(NoWarn);CS1591 true Autofac.Test.Scenarios.ScannedAssembly @@ -9,7 +9,6 @@ true true Autofac.Test.Scenarios.ScannedAssembly - 1.6.0 false ../../build/Analyzers.ruleset diff --git a/test/Autofac.Test/Autofac.Test.csproj b/test/Autofac.Test/Autofac.Test.csproj index b7b076403..d1de44c41 100644 --- a/test/Autofac.Test/Autofac.Test.csproj +++ b/test/Autofac.Test/Autofac.Test.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net46 + netcoreapp3.0;net472 $(NoWarn);CS1591;SA1602;SA1611 true ../../Autofac.snk @@ -10,6 +10,7 @@ true ../../build/Analyzers.ruleset false + latest @@ -35,15 +36,11 @@ - + $(DefineConstants);PARTIAL_TRUST - - - ASYNC_DISPOSE_AVAILABLE;$(DefineConstants) - - - + + diff --git a/test/Autofac.Test/Core/ContainerTests.cs b/test/Autofac.Test/Core/ContainerTests.cs index de9ebf533..56954c2d4 100644 --- a/test/Autofac.Test/Core/ContainerTests.cs +++ b/test/Autofac.Test/Core/ContainerTests.cs @@ -179,7 +179,6 @@ public void ReplaceInstance_ModuleActivatingHandlerProvidesResultToRelease() } } -#if ASYNC_DISPOSE_AVAILABLE [Fact] public async ValueTask AsyncContainerDisposeTriggersAsyncServiceDispose() { @@ -199,7 +198,6 @@ public async ValueTask AsyncContainerDisposeTriggersAsyncServiceDispose() Assert.False(tracker.IsSyncDisposed); Assert.True(tracker.IsAsyncDisposed); } -#endif private class ReplaceInstanceModule : Module { diff --git a/test/Autofac.Test/Core/DisposerTests.cs b/test/Autofac.Test/Core/DisposerTests.cs index d4d433ce5..6d73b3600 100644 --- a/test/Autofac.Test/Core/DisposerTests.cs +++ b/test/Autofac.Test/Core/DisposerTests.cs @@ -58,7 +58,6 @@ public void CannotAddObjectsToDisposerAfterSyncDispose() }); } -#if ASYNC_DISPOSE_AVAILABLE [Fact] public void DisposerDisposesOfObjectsAsyncIfIAsyncDisposableDeclared() { @@ -170,6 +169,5 @@ public void DisposerAsyncDisposesContainedInstances_InReverseOfOrderAdded() o3 => Assert.Same(syncInstance3, o3), o4 => Assert.Same(asyncInstance1, o4)); } -#endif } } diff --git a/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs b/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs index bf511c867..6a6b5d12b 100644 --- a/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs +++ b/test/Autofac.Test/Core/Lifetime/LifetimeScopeTests.cs @@ -102,7 +102,6 @@ public void NestedLifetimeScopesMaintainServiceLimitTypes() } } -#if ASYNC_DISPOSE_AVAILABLE [Fact] public async ValueTask AsyncDisposeLifetimeScopeDisposesRegistrationsAsync() { @@ -163,7 +162,6 @@ public void DisposeLifetimeScopeDisposesRegistrationsThatAreAsyncAndSyncDispose( Assert.False(asyncTracker.IsAsyncDisposed); Assert.True(asyncTracker.IsSyncDisposed); } -#endif internal class DependsOnRegisteredInstance { diff --git a/test/Autofac.Test/Util/AsyncDisposeTracker.cs b/test/Autofac.Test/Util/AsyncDisposeTracker.cs index 2b9e21cd4..652020c75 100644 --- a/test/Autofac.Test/Util/AsyncDisposeTracker.cs +++ b/test/Autofac.Test/Util/AsyncDisposeTracker.cs @@ -3,7 +3,6 @@ namespace Autofac.Test.Util { -#if ASYNC_DISPOSE_AVAILABLE public class AsyncDisposeTracker : IDisposable, IAsyncDisposable { public event EventHandler Disposing; @@ -34,5 +33,4 @@ public async ValueTask DisposeAsync() } } } -#endif } diff --git a/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs b/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs index 605d5b568..052a3edc1 100644 --- a/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs +++ b/test/Autofac.Test/Util/AsyncOnlyDisposeTracker.cs @@ -3,7 +3,6 @@ namespace Autofac.Test.Util { -#if ASYNC_DISPOSE_AVAILABLE public class AsyncOnlyDisposeTracker : IAsyncDisposable { public event EventHandler Disposing; @@ -22,5 +21,4 @@ public async ValueTask DisposeAsync() } } } -#endif } From c888c16aaaad4c71505fff06636bed9cf3215ea0 Mon Sep 17 00:00:00 2001 From: alistairjevans Date: Sun, 20 Oct 2019 11:13:27 +0100 Subject: [PATCH 7/8] Add net461 target (after checking the MS guidance), fix the Lazy reference problems and update/remove other incompatible #if statements after the targeting changes. --- src/Autofac/Autofac.csproj | 8 ++++++-- .../Core/DependencyResolutionException.cs | 6 ------ .../LazyWithMetadataRegistrationSource.cs | 18 +----------------- .../Autofac.Specification.Test.csproj | 6 +++++- .../Features/DecoratorTests.cs | 2 -- test/Autofac.Test/Autofac.Test.csproj | 6 +++--- 6 files changed, 15 insertions(+), 31 deletions(-) diff --git a/src/Autofac/Autofac.csproj b/src/Autofac/Autofac.csproj index d4ec74147..f79bd457d 100644 --- a/src/Autofac/Autofac.csproj +++ b/src/Autofac/Autofac.csproj @@ -3,7 +3,7 @@ Autofac is an IoC container for Microsoft .NET. It manages the dependencies between classes so that applications stay easy to change as they grow in size and complexity. 5.0.0 - netstandard2.1;netstandard2.0 + netstandard2.1;netstandard2.0;net461 latest $(NoWarn);CS1591;IDE0008 true @@ -50,10 +50,14 @@ - + + + + + True diff --git a/src/Autofac/Core/DependencyResolutionException.cs b/src/Autofac/Core/DependencyResolutionException.cs index 80eb19238..b53407479 100644 --- a/src/Autofac/Core/DependencyResolutionException.cs +++ b/src/Autofac/Core/DependencyResolutionException.cs @@ -24,9 +24,7 @@ // OTHER DEALINGS IN THE SOFTWARE. using System; -#if !NETSTANDARD1_1 using System.Runtime.Serialization; -#endif namespace Autofac.Core { @@ -36,17 +34,13 @@ namespace Autofac.Core /// been made during the operation. For example, 'on activated' handlers may have already been /// fired, or 'single instance' components partially constructed. /// -#if !NETSTANDARD1_1 [Serializable] -#endif public class DependencyResolutionException : Exception { -#if !NETSTANDARD1_1 protected DependencyResolutionException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif /// /// Initializes a new instance of the class. diff --git a/src/Autofac/Features/LazyDependencies/LazyWithMetadataRegistrationSource.cs b/src/Autofac/Features/LazyDependencies/LazyWithMetadataRegistrationSource.cs index 8f2703188..79e566a33 100644 --- a/src/Autofac/Features/LazyDependencies/LazyWithMetadataRegistrationSource.cs +++ b/src/Autofac/Features/LazyDependencies/LazyWithMetadataRegistrationSource.cs @@ -53,15 +53,10 @@ public IEnumerable RegistrationsFor(Service service, Fun throw new ArgumentNullException(nameof(registrationAccessor)); var swt = service as IServiceWithType; -#if NET45 - var lazyType = GetLazyType(swt); - if (swt == null || lazyType == null || !swt.ServiceType.IsGenericTypeDefinedBy(lazyType)) - return Enumerable.Empty(); -#else + var lazyType = typeof(Lazy<,>); if (swt == null || !swt.ServiceType.IsGenericTypeDefinedBy(lazyType)) return Enumerable.Empty(); -#endif var genericTypeArguments = swt.ServiceType.GetTypeInfo().GenericTypeArguments.ToArray(); var valueType = genericTypeArguments[0]; @@ -104,16 +99,5 @@ private IComponentRegistration CreateLazyRegistration(Service provided return rb.CreateRegistration(); } - -#if NET45 - private static Type GetLazyType(IServiceWithType serviceWithType) - { - return serviceWithType != null - && serviceWithType.ServiceType.GetTypeInfo().IsGenericType - && serviceWithType.ServiceType.GetGenericTypeDefinition().FullName == "System.Lazy`2" - ? serviceWithType.ServiceType.GetGenericTypeDefinition() - : null; - } -#endif } } diff --git a/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj b/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj index b69eea8c9..629e23ce6 100644 --- a/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj +++ b/test/Autofac.Specification.Test/Autofac.Specification.Test.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + netcoreapp3.0;net461 $(NoWarn);CS1591;SA1602;SA1611 true ../../build/Analyzers.ruleset @@ -14,6 +14,10 @@ + + + + diff --git a/test/Autofac.Specification.Test/Features/DecoratorTests.cs b/test/Autofac.Specification.Test/Features/DecoratorTests.cs index ff856586b..5b6cf28e4 100644 --- a/test/Autofac.Specification.Test/Features/DecoratorTests.cs +++ b/test/Autofac.Specification.Test/Features/DecoratorTests.cs @@ -152,7 +152,6 @@ public void CanResolveDecoratorWithLazy() Assert.IsType(decoratedService.Decorated); } -#if NETCOREAPP2_1 [Fact] public void CanResolveDecoratorWithLazyWithMetadata() { @@ -168,7 +167,6 @@ public void CanResolveDecoratorWithLazyWithMetadata() Assert.IsType(decoratedService.Decorated); Assert.Equal(123, meta.Metadata.A); } -#endif [Fact] public void CanResolveDecoratorWithOwned() diff --git a/test/Autofac.Test/Autofac.Test.csproj b/test/Autofac.Test/Autofac.Test.csproj index d1de44c41..dd876a864 100644 --- a/test/Autofac.Test/Autofac.Test.csproj +++ b/test/Autofac.Test/Autofac.Test.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + netcoreapp3.0;net461 $(NoWarn);CS1591;SA1602;SA1611 true ../../Autofac.snk @@ -36,11 +36,11 @@ - + $(DefineConstants);PARTIAL_TRUST - + From fd73035a0c1646689cb2023a24c974a79903c86c Mon Sep 17 00:00:00 2001 From: alistairjevans Date: Sun, 20 Oct 2019 13:25:36 +0100 Subject: [PATCH 8/8] Use await in the Disposer Tests. --- test/Autofac.Test/Core/DisposerTests.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/Autofac.Test/Core/DisposerTests.cs b/test/Autofac.Test/Core/DisposerTests.cs index 6d73b3600..668d1a058 100644 --- a/test/Autofac.Test/Core/DisposerTests.cs +++ b/test/Autofac.Test/Core/DisposerTests.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Threading.Tasks; using Autofac.Core; using Autofac.Test.Util; using Xunit; @@ -59,7 +59,7 @@ public void CannotAddObjectsToDisposerAfterSyncDispose() } [Fact] - public void DisposerDisposesOfObjectsAsyncIfIAsyncDisposableDeclared() + public async ValueTask DisposerDisposesOfObjectsAsyncIfIAsyncDisposableDeclared() { var instance = new AsyncDisposeTracker(); @@ -74,21 +74,21 @@ public void DisposerDisposesOfObjectsAsyncIfIAsyncDisposableDeclared() Assert.False(instance.IsAsyncDisposed); // Now we wait. - result.GetAwaiter().GetResult(); + await result; Assert.False(instance.IsSyncDisposed); Assert.True(instance.IsAsyncDisposed); } [Fact] - public void DisposerDisposesOfObjectsSyncIfIDisposableOnly() + public async ValueTask DisposerDisposesOfObjectsSyncIfIDisposableOnly() { var instance = new DisposeTracker(); var disposer = new Disposer(); disposer.AddInstanceForDisposal(instance); Assert.False(instance.IsDisposed); - disposer.DisposeAsync().GetAwaiter().GetResult(); + await disposer.DisposeAsync(); Assert.True(instance.IsDisposed); } @@ -107,7 +107,7 @@ public void DisposerDisposesOfObjectsSyncIfIAsyncDisposableDeclaredButSyncDispos } [Fact] - public void CannotAddObjectsToDisposerAfterAsyncDispose() + public async ValueTask CannotAddObjectsToDisposerAfterAsyncDispose() { var instance = new AsyncDisposeTracker(); @@ -115,7 +115,7 @@ public void CannotAddObjectsToDisposerAfterAsyncDispose() disposer.AddInstanceForDisposal(instance); Assert.False(instance.IsSyncDisposed); Assert.False(instance.IsAsyncDisposed); - disposer.DisposeAsync().GetAwaiter().GetResult(); + await disposer.DisposeAsync(); Assert.False(instance.IsSyncDisposed); Assert.True(instance.IsAsyncDisposed); @@ -140,7 +140,7 @@ public void SyncDisposalOnObjectWithNoIDisposableThrows() } [Fact] - public void DisposerAsyncDisposesContainedInstances_InReverseOfOrderAdded() + public async ValueTask DisposerAsyncDisposesContainedInstances_InReverseOfOrderAdded() { var disposeOrder = new List(); @@ -160,7 +160,7 @@ public void DisposerAsyncDisposesContainedInstances_InReverseOfOrderAdded() disposer.AddInstanceForDisposal(syncInstance4); disposer.AddInstanceForAsyncDisposal(asyncOnlyInstance2); - disposer.DisposeAsync().GetAwaiter().GetResult(); + await disposer.DisposeAsync(); Assert.Collection( disposeOrder,