From 3528e9a2859074e5bd4168704ff3fa0de0bec3f8 Mon Sep 17 00:00:00 2001 From: Matt Oswald Date: Mon, 1 Feb 2016 21:15:13 -0600 Subject: [PATCH 1/2] adding the async types and making it compile again --- src/LightInject.Tests/AssemblyScannerTests.cs | 19 ++--- .../CompositionRootExecutorMock.cs | 7 ++ .../CompositionRootExecutorTests.cs | 12 +-- src/LightInject.Tests/CompositionRootMock.cs | 18 +++-- src/LightInject.Tests/ContainerMock.cs | 7 +- .../LazyCompositionRootTests.cs | 3 +- src/LightInject/LightInject.cs | 75 ++++++++++++++++++- 7 files changed, 116 insertions(+), 25 deletions(-) diff --git a/src/LightInject.Tests/AssemblyScannerTests.cs b/src/LightInject.Tests/AssemblyScannerTests.cs index ca9336a5..19032fcc 100644 --- a/src/LightInject.Tests/AssemblyScannerTests.cs +++ b/src/LightInject.Tests/AssemblyScannerTests.cs @@ -1,33 +1,32 @@ -using System.Management.Instrumentation; using System.Reflection; namespace LightInject.Tests { using System; - using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using LightInject; using LightInject.SampleLibrary; - + using LightMock; using Xunit; - - - + + + public class AssemblyScannerTests { private MockContext GetContainerMock(Func lifetimeFactory, Func shouldRegister) { var containerMock = new ContainerMock(); var compositionRootMock = new CompositionRootMock(); + var asyncCompositionRootMock = new AsyncCompositionRootMock(); var compositionRootTypeExtractorMock = new TypeExtractorMock(); compositionRootTypeExtractorMock.Arrange(c => c.Execute(The.IsAnyValue)).Returns(Type.EmptyTypes); - var assemblyScanner = new AssemblyScanner(new ConcreteTypeExtractor(), compositionRootTypeExtractorMock, new CompositionRootExecutor(containerMock,t => compositionRootMock)); + var assemblyScanner = new AssemblyScanner(new ConcreteTypeExtractor(), compositionRootTypeExtractorMock, new CompositionRootExecutor(containerMock,t => compositionRootMock,t => asyncCompositionRootMock)); assemblyScanner.Scan(typeof(IFoo).Assembly, containerMock, lifetimeFactory, shouldRegister); return containerMock; } @@ -101,12 +100,13 @@ public void Scan_SampleAssembly_ConfiguresAllServicesByDefault() public void Scan_SampleAssemblyWithCompositionRoot_CallsComposeMethodOnce() { var compositionRootMock = new CompositionRootMock(); + var asyncCompositionRootMock = new AsyncCompositionRootMock(); var containerMock = new ContainerMock(); var compositionRootExtractorMock = new TypeExtractorMock(); compositionRootExtractorMock.Arrange(c => c.Execute(The.IsAnyValue)).Returns(new []{typeof(CompositionRootMock)}); var assemblyScanner = new AssemblyScanner(new ConcreteTypeExtractor(), compositionRootExtractorMock, - new CompositionRootExecutor(containerMock, t => compositionRootMock)); + new CompositionRootExecutor(containerMock, t => compositionRootMock, t => asyncCompositionRootMock)); assemblyScanner.Scan(typeof(AssemblyScannerTests).Assembly, containerMock); @@ -117,10 +117,11 @@ public void Scan_SampleAssemblyWithCompositionRoot_CallsComposeMethodOnce() public void ScanUsingPredicate_SampleAssemblyWithCompositionRoot_DoesNotCallCompositionRoot() { var compositionRootMock = new CompositionRootMock(); + var asyncCompositionRootMock = new AsyncCompositionRootMock(); var containerMock = new ContainerMock(); var assemblyScanner = new AssemblyScanner(new ConcreteTypeExtractor(), new CompositionRootTypeExtractor(new CompositionRootAttributeExtractor()), - new CompositionRootExecutor(containerMock, t => compositionRootMock)); + new CompositionRootExecutor(containerMock, t => compositionRootMock, t => asyncCompositionRootMock)); assemblyScanner.Scan(typeof(AssemblyScannerTests).Assembly, containerMock, () => null, (s, t) => true); diff --git a/src/LightInject.Tests/CompositionRootExecutorMock.cs b/src/LightInject.Tests/CompositionRootExecutorMock.cs index e7f397f2..bcd7d883 100644 --- a/src/LightInject.Tests/CompositionRootExecutorMock.cs +++ b/src/LightInject.Tests/CompositionRootExecutorMock.cs @@ -3,11 +3,18 @@ namespace LightInject.Tests { + using System.Threading.Tasks; + internal class CompositionRootExecutorMock : MockContext, ICompositionRootExecutor { public void Execute(Type compositionRootType) { ((IInvocationContext) this).Invoke(c => c.Execute(compositionRootType)); } + + public Task ExecuteAsync(Type compositionRootType) + { + return ((IInvocationContext) this).Invoke(c => c.ExecuteAsync(compositionRootType)); + } } } \ No newline at end of file diff --git a/src/LightInject.Tests/CompositionRootExecutorTests.cs b/src/LightInject.Tests/CompositionRootExecutorTests.cs index f2df1de1..4b0e111d 100644 --- a/src/LightInject.Tests/CompositionRootExecutorTests.cs +++ b/src/LightInject.Tests/CompositionRootExecutorTests.cs @@ -1,16 +1,17 @@ namespace LightInject.Tests -{ +{ using LightMock; - using Xunit; - + using Xunit; + public class CompositionRootExecutorTests { [Fact] public void Execute_CompositionRootType_IsCreatedAndExecuted() { CompositionRootMock compositionRootMock = new CompositionRootMock(); + AsyncCompositionRootMock asyncCompositionRootMock = new AsyncCompositionRootMock(); var serviceContainerMock = new ContainerMock(); - var executor = new CompositionRootExecutor(serviceContainerMock, (t) => compositionRootMock); + var executor = new CompositionRootExecutor(serviceContainerMock, (t) => compositionRootMock, (t) => asyncCompositionRootMock); executor.Execute(typeof(CompositionRootMock)); compositionRootMock.Assert(c => c.Compose(The.IsAnyValue), Invoked.Once); } @@ -19,8 +20,9 @@ public void Execute_CompositionRootType_IsCreatedAndExecuted() public void Execute_CompositionRootType_IsCreatedAndExecutedOnlyOnce() { CompositionRootMock compositionRootMock = new CompositionRootMock(); + AsyncCompositionRootMock asyncCompositionRootMock = new AsyncCompositionRootMock(); var serviceContainerMock = new ContainerMock(); - var executor = new CompositionRootExecutor(serviceContainerMock, (t) => compositionRootMock); + var executor = new CompositionRootExecutor(serviceContainerMock, (t) => compositionRootMock, (t) => asyncCompositionRootMock); executor.Execute(typeof(CompositionRootMock)); executor.Execute(typeof(CompositionRootMock)); compositionRootMock.Assert(c => c.Compose(The.IsAnyValue), Invoked.Once); diff --git a/src/LightInject.Tests/CompositionRootMock.cs b/src/LightInject.Tests/CompositionRootMock.cs index 54e8444b..18ecc416 100644 --- a/src/LightInject.Tests/CompositionRootMock.cs +++ b/src/LightInject.Tests/CompositionRootMock.cs @@ -1,14 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using LightMock; namespace LightInject.Tests -{ - - +{ + + internal class CompositionRootMock : MockContext, ICompositionRoot { public void Compose(IServiceRegistry serviceRegistry) @@ -24,4 +20,12 @@ public void Compose(IServiceRegistry serviceRegistry) ((IInvocationContext)this).Invoke(c => c.Compose(serviceRegistry)); } } + + internal class AsyncCompositionRootMock : MockContext, IAsyncCompositionRoot + { + public Task ComposeAsync(IServiceRegistry serviceRegistry) + { + return ((IInvocationContext)this).Invoke(c => c.ComposeAsync(serviceRegistry)); + } + } } diff --git a/src/LightInject.Tests/ContainerMock.cs b/src/LightInject.Tests/ContainerMock.cs index 69d2487b..5c4af914 100644 --- a/src/LightInject.Tests/ContainerMock.cs +++ b/src/LightInject.Tests/ContainerMock.cs @@ -2,8 +2,8 @@ namespace LightInject.Tests { using System; using System.Collections.Generic; - using System.Linq.Expressions; using System.Reflection; + using System.Threading.Tasks; using LightMock; internal class ContainerMock : MockContext, IServiceContainer @@ -192,6 +192,11 @@ public void RegisterAssembly(Assembly assembly, Func lifetimeFactory, throw new NotImplementedException(); } + public Task RegisterFromAsync() where TAsyncCompositionRoot : IAsyncCompositionRoot, new() + { + throw new NotImplementedException(); + } + public void RegisterConstructorDependency(Func factory) { throw new NotImplementedException(); diff --git a/src/LightInject.Tests/LazyCompositionRootTests.cs b/src/LightInject.Tests/LazyCompositionRootTests.cs index 63a4e2e1..f5e58726 100644 --- a/src/LightInject.Tests/LazyCompositionRootTests.cs +++ b/src/LightInject.Tests/LazyCompositionRootTests.cs @@ -21,13 +21,14 @@ public void GetInstance_UnknownService_ExecutesCompositionRootInSourceAssembly() { var container = new ServiceContainer(); var compositionRootMock = new CompositionRootMock(); + var asyncCompositionRootMock = new AsyncCompositionRootMock(); compositionRootMock.Arrange(c => c.Compose(container)).Callback(c => c.Register()); var compositionRootTypeExtractorMock = new TypeExtractorMock(); compositionRootTypeExtractorMock.Arrange(c => c.Execute(The.IsAnyValue)).Returns(new[] {typeof(CompositionRootMock)}); var assemblyScanner = new AssemblyScanner(new ConcreteTypeExtractor(), compositionRootTypeExtractorMock, - new CompositionRootExecutor(container, t => compositionRootMock)); + new CompositionRootExecutor(container, t => compositionRootMock, t => asyncCompositionRootMock)); container.AssemblyScanner = assemblyScanner; diff --git a/src/LightInject/LightInject.cs b/src/LightInject/LightInject.cs index d2f0f36b..61e8dc7d 100644 --- a/src/LightInject/LightInject.cs +++ b/src/LightInject/LightInject.cs @@ -57,6 +57,7 @@ namespace LightInject using System.Text; using System.Text.RegularExpressions; using System.Threading; + using System.Threading.Tasks; /// /// A delegate that represent the dynamic method compiled to resolved service instances. @@ -396,6 +397,14 @@ void Register(string serviceName, ILifetime lifetime) void RegisterFrom() where TCompositionRoot : ICompositionRoot, new(); + /// + /// Asynchronously registers services from the given type. + /// + /// The type of to register from. + /// A Task indicating registration has completed. + Task RegisterFromAsync() + where TAsyncCompositionRoot : IAsyncCompositionRoot, new(); + /// /// Registers a factory delegate to be used when resolving a constructor dependency for /// a implicitly registered service. @@ -777,6 +786,19 @@ public interface ICompositionRoot void Compose(IServiceRegistry serviceRegistry); } + /// + /// Represents a class that acts as an asynchronous composition root for an instance. + /// + public interface IAsyncCompositionRoot + { + /// + /// Composes services by adding services to the . + /// + /// The target . + /// /// A Task indicating composition has completed. + Task ComposeAsync(IServiceRegistry serviceRegistry); + } + /// /// Represents a class that extracts a set of types from an . /// @@ -954,6 +976,13 @@ public interface ICompositionRootExecutor /// /// The concrete type to be instantiated and executed. void Execute(Type compositionRootType); + + /// + /// Creates an instance of the and executes the method. + /// + /// The concrete type to be instantiated and executed. + /// A task indicating the IAsyncCompositionRoot execution has completed. + Task ExecuteAsync(Type asyncCompositionRootType); } /// @@ -1641,7 +1670,7 @@ public ServiceContainer() options = ContainerOptions.Default; var concreteTypeExtractor = new CachedTypeExtractor(new ConcreteTypeExtractor()); CompositionRootTypeExtractor = new CachedTypeExtractor(new CompositionRootTypeExtractor(new CompositionRootAttributeExtractor())); - CompositionRootExecutor = new CompositionRootExecutor(this, type => (ICompositionRoot)Activator.CreateInstance(type)); + CompositionRootExecutor = new CompositionRootExecutor(this, type => (ICompositionRoot)Activator.CreateInstance(type), type => (IAsyncCompositionRoot)Activator.CreateInstance(type)); AssemblyScanner = new AssemblyScanner(concreteTypeExtractor, CompositionRootTypeExtractor, CompositionRootExecutor); PropertyDependencySelector = new PropertyDependencySelector(new PropertySelector()); ConstructorDependencySelector = new ConstructorDependencySelector(); @@ -1920,6 +1949,17 @@ public void RegisterFrom() CompositionRootExecutor.Execute(typeof(TCompositionRoot)); } + /// + /// Asynchronously registers services from the given type. + /// + /// The type of to register from. + /// A Task indicating the composition has completed. + public Task RegisterFromAsync() + where TAsyncCompositionRoot : IAsyncCompositionRoot, new() + { + return CompositionRootExecutor.ExecuteAsync(typeof(TAsyncCompositionRoot)); + } + /// /// Registers a factory delegate to be used when resolving a constructor dependency for /// a implicitly registered service. @@ -5984,6 +6024,7 @@ public class CompositionRootExecutor : ICompositionRootExecutor { private readonly IServiceRegistry serviceRegistry; private readonly Func activator; + private readonly Func asyncActivator; private readonly IList executedCompositionRoots = new List(); @@ -5994,10 +6035,12 @@ public class CompositionRootExecutor : ICompositionRootExecutor /// /// The to be configured by the . /// The function delegate that is responsible for creating an instance of the . - public CompositionRootExecutor(IServiceRegistry serviceRegistry, Func activator) + /// The function delegate that is responsible for creating an instance of the . + public CompositionRootExecutor(IServiceRegistry serviceRegistry, Func activator, Func asyncActivator) { this.serviceRegistry = serviceRegistry; this.activator = activator; + this.asyncActivator = asyncActivator; } /// @@ -6019,6 +6062,34 @@ public void Execute(Type compositionRootType) } } } + + /// + /// Creates an instance of the and executes the method. + /// + /// The concrete type to be instantiated and executed. + /// A task indicating composition has completed. + public async Task ExecuteAsync(Type asyncCompositionRootType) + { + if (!executedCompositionRoots.Contains(asyncCompositionRootType)) + { + var wasAdded = false; + + lock (syncRoot) + { + if (!executedCompositionRoots.Contains(asyncCompositionRootType)) + { + wasAdded = true; + executedCompositionRoots.Add(asyncCompositionRootType); + } + } + + if (wasAdded) + { + var asyncCompositionRoot = asyncActivator(asyncCompositionRootType); + await asyncCompositionRoot.ComposeAsync(serviceRegistry); + } + } + } } /// From fd61732b7ed82b7facef6b27af8fa14e8fcf88d5 Mon Sep 17 00:00:00 2001 From: Matt Oswald Date: Mon, 1 Feb 2016 21:48:41 -0600 Subject: [PATCH 2/2] added the direct tests for ExecuteAsync and RegisterFromAsync --- .../CompositionRootExecutorMock.cs | 3 +- .../CompositionRootExecutorTests.cs | 46 +++++++++++++++++-- src/LightInject.Tests/CompositionRootMock.cs | 9 ++-- .../ServiceContainerTests.cs | 27 +++++++---- 4 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/LightInject.Tests/CompositionRootExecutorMock.cs b/src/LightInject.Tests/CompositionRootExecutorMock.cs index bcd7d883..fcd598c8 100644 --- a/src/LightInject.Tests/CompositionRootExecutorMock.cs +++ b/src/LightInject.Tests/CompositionRootExecutorMock.cs @@ -14,7 +14,8 @@ public void Execute(Type compositionRootType) public Task ExecuteAsync(Type compositionRootType) { - return ((IInvocationContext) this).Invoke(c => c.ExecuteAsync(compositionRootType)); + ((IInvocationContext) this).Invoke(c => c.ExecuteAsync(compositionRootType)); + return Task.CompletedTask; } } } \ No newline at end of file diff --git a/src/LightInject.Tests/CompositionRootExecutorTests.cs b/src/LightInject.Tests/CompositionRootExecutorTests.cs index 4b0e111d..a00c1823 100644 --- a/src/LightInject.Tests/CompositionRootExecutorTests.cs +++ b/src/LightInject.Tests/CompositionRootExecutorTests.cs @@ -1,8 +1,9 @@ namespace LightInject.Tests -{ +{ + using System.Threading.Tasks; using LightMock; - using Xunit; - + using Xunit; + public class CompositionRootExecutorTests { [Fact] @@ -27,5 +28,44 @@ public void Execute_CompositionRootType_IsCreatedAndExecutedOnlyOnce() executor.Execute(typeof(CompositionRootMock)); compositionRootMock.Assert(c => c.Compose(The.IsAnyValue), Invoked.Once); } + + [Fact] + public async Task ExecuteAsync_CompositionRootType_IsCreatedAndExecuted() + { + CompositionRootMock compositionRootMock = new CompositionRootMock(); + AsyncCompositionRootMock asyncCompositionRootMock = new AsyncCompositionRootMock(); + var serviceContainerMock = new ContainerMock(); + var executor = new CompositionRootExecutor(serviceContainerMock, (t) => compositionRootMock, (t) => asyncCompositionRootMock); + await executor.ExecuteAsync(typeof(AsyncCompositionRootMock)); + asyncCompositionRootMock.Assert(c => c.ComposeAsync(The.IsAnyValue), Invoked.Once); + } + + [Fact] + public async Task ExecuteAsync_CompositionRootType_IsCreatedAndExecutedOnlyOnce() + { + CompositionRootMock compositionRootMock = new CompositionRootMock(); + AsyncCompositionRootMock asyncCompositionRootMock = new AsyncCompositionRootMock(); + var serviceContainerMock = new ContainerMock(); + var executor = new CompositionRootExecutor(serviceContainerMock, (t) => compositionRootMock, (t) => asyncCompositionRootMock); + await executor.ExecuteAsync(typeof(AsyncCompositionRootMock)); + await executor.ExecuteAsync(typeof(AsyncCompositionRootMock)); + asyncCompositionRootMock.Assert(c => c.ComposeAsync(The.IsAnyValue), Invoked.Once); + } + + [Fact] + public async Task ExecuteAsync_CompositionRootType_IsCreatedAndExecutedOnlyOnceWithParallel() + { + CompositionRootMock compositionRootMock = new CompositionRootMock(); + AsyncCompositionRootMock asyncCompositionRootMock = new AsyncCompositionRootMock(); + var serviceContainerMock = new ContainerMock(); + var executor = new CompositionRootExecutor(serviceContainerMock, (t) => compositionRootMock, (t) => asyncCompositionRootMock); + + var first = executor.ExecuteAsync(typeof(AsyncCompositionRootMock)); + var second = executor.ExecuteAsync(typeof(AsyncCompositionRootMock)); + + await Task.WhenAll(first, second); + + asyncCompositionRootMock.Assert(c => c.ComposeAsync(The.IsAnyValue), Invoked.Once); + } } } \ No newline at end of file diff --git a/src/LightInject.Tests/CompositionRootMock.cs b/src/LightInject.Tests/CompositionRootMock.cs index 18ecc416..0064f8da 100644 --- a/src/LightInject.Tests/CompositionRootMock.cs +++ b/src/LightInject.Tests/CompositionRootMock.cs @@ -2,9 +2,9 @@ using LightMock; namespace LightInject.Tests -{ - - +{ + + internal class CompositionRootMock : MockContext, ICompositionRoot { public void Compose(IServiceRegistry serviceRegistry) @@ -25,7 +25,8 @@ internal class AsyncCompositionRootMock : MockContext, IA { public Task ComposeAsync(IServiceRegistry serviceRegistry) { - return ((IInvocationContext)this).Invoke(c => c.ComposeAsync(serviceRegistry)); + ((IInvocationContext)this).Invoke(c => c.ComposeAsync(serviceRegistry)); + return Task.CompletedTask; } } } diff --git a/src/LightInject.Tests/ServiceContainerTests.cs b/src/LightInject.Tests/ServiceContainerTests.cs index cb103d8d..2836dbe6 100644 --- a/src/LightInject.Tests/ServiceContainerTests.cs +++ b/src/LightInject.Tests/ServiceContainerTests.cs @@ -5,21 +5,17 @@ namespace LightInject.Tests { using System; using System.Collections.Generic; - using System.Linq; - using System.Security; - + using System.Linq; using System.Text; using System.Threading.Tasks; - using LightInject; using LightInject.SampleLibrary; using Xunit; - using Xunit.Sdk; + using Bar = LightInject.SampleLibrary.Bar; using Foo = LightInject.SampleLibrary.Foo; - using IFoo = LightInject.SampleLibrary.IFoo; using IBar = LightInject.SampleLibrary.IBar; - using Bar = LightInject.SampleLibrary.Bar; - + using IFoo = LightInject.SampleLibrary.IFoo; + public class ServiceContainerTests : TestBase { #region InputValidation @@ -1600,7 +1596,20 @@ public void RegisterFrom_CompositionRoot_CallsCompositionRootExecutor() compositionRootExecutorMock.Assert(c => c.Execute(typeof(CompositionRootMock)), Invoked.Once); } -#endif +#endif + + [Fact] + public async Task RegisterFromAsync_CompositionRoot_CallsCompositionRootExecutor() + { + var container = (ServiceContainer)CreateContainer(); + var compositionRootExecutorMock = new CompositionRootExecutorMock(); + container.CompositionRootExecutor = compositionRootExecutorMock; + + await container.RegisterFromAsync(); + + compositionRootExecutorMock.Assert(c => c.ExecuteAsync(typeof(AsyncCompositionRootMock)), Invoked.Once); + } + [Fact] public void GetInstance_SingletonInstance_EmitterIsProperlyPoppedOnConstructorException() {