Skip to content

Async LightInject: adding async CompositionRoots #256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
adding the async types and making it compile again
  • Loading branch information
moswald committed Feb 2, 2016
commit 3528e9a2859074e5bd4168704ff3fa0de0bec3f8
19 changes: 10 additions & 9 deletions src/LightInject.Tests/AssemblyScannerTests.cs
Original file line number Diff line number Diff line change
@@ -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<IServiceContainer> GetContainerMock(Func<ILifetime> lifetimeFactory, Func<Type, Type, bool> shouldRegister)
{
var containerMock = new ContainerMock();
var compositionRootMock = new CompositionRootMock();
var asyncCompositionRootMock = new AsyncCompositionRootMock();

var compositionRootTypeExtractorMock = new TypeExtractorMock();
compositionRootTypeExtractorMock.Arrange(c => c.Execute(The<Assembly>.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;
}
Expand Down Expand Up @@ -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<Assembly>.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);

Expand All @@ -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);

Expand Down
7 changes: 7 additions & 0 deletions src/LightInject.Tests/CompositionRootExecutorMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@

namespace LightInject.Tests
{
using System.Threading.Tasks;

internal class CompositionRootExecutorMock : MockContext<ICompositionRootExecutor>, ICompositionRootExecutor
{
public void Execute(Type compositionRootType)
{
((IInvocationContext<ICompositionRootExecutor>) this).Invoke(c => c.Execute(compositionRootType));
}

public Task ExecuteAsync(Type compositionRootType)
{
return ((IInvocationContext<ICompositionRootExecutor>) this).Invoke(c => c.ExecuteAsync(compositionRootType));
}
}
}
12 changes: 7 additions & 5 deletions src/LightInject.Tests/CompositionRootExecutorTests.cs
Original file line number Diff line number Diff line change
@@ -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<IServiceContainer>.IsAnyValue), Invoked.Once);
}
Expand All @@ -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<IServiceContainer>.IsAnyValue), Invoked.Once);
Expand Down
18 changes: 11 additions & 7 deletions src/LightInject.Tests/CompositionRootMock.cs
Original file line number Diff line number Diff line change
@@ -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>, ICompositionRoot
{
public void Compose(IServiceRegistry serviceRegistry)
Expand All @@ -24,4 +20,12 @@ public void Compose(IServiceRegistry serviceRegistry)
((IInvocationContext<ICompositionRoot>)this).Invoke(c => c.Compose(serviceRegistry));
}
}

internal class AsyncCompositionRootMock : MockContext<IAsyncCompositionRoot>, IAsyncCompositionRoot
{
public Task ComposeAsync(IServiceRegistry serviceRegistry)
{
return ((IInvocationContext<IAsyncCompositionRoot>)this).Invoke(c => c.ComposeAsync(serviceRegistry));
}
}
}
7 changes: 6 additions & 1 deletion src/LightInject.Tests/ContainerMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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>, IServiceContainer
Expand Down Expand Up @@ -192,6 +192,11 @@ public void RegisterAssembly(Assembly assembly, Func<ILifetime> lifetimeFactory,
throw new NotImplementedException();
}

public Task RegisterFromAsync<TAsyncCompositionRoot>() where TAsyncCompositionRoot : IAsyncCompositionRoot, new()
{
throw new NotImplementedException();
}

public void RegisterConstructorDependency<TDependency>(Func<IServiceFactory, ParameterInfo, TDependency> factory)
{
throw new NotImplementedException();
Expand Down
3 changes: 2 additions & 1 deletion src/LightInject.Tests/LazyCompositionRootTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IServiceContainer>(c => c.Register<IFoo, Foo>());

var compositionRootTypeExtractorMock = new TypeExtractorMock();
compositionRootTypeExtractorMock.Arrange(c => c.Execute(The<Assembly>.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;

Expand Down
75 changes: 73 additions & 2 deletions src/LightInject/LightInject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ namespace LightInject
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// A delegate that represent the dynamic method compiled to resolved service instances.
Expand Down Expand Up @@ -396,6 +397,14 @@ void Register<TService, TImplementation>(string serviceName, ILifetime lifetime)
void RegisterFrom<TCompositionRoot>()
where TCompositionRoot : ICompositionRoot, new();

/// <summary>
/// Asynchronously registers services from the given <typeparamref name="TAsyncCompositionRoot"/> type.
/// </summary>
/// <typeparam name="TAsyncCompositionRoot">The type of <see cref="IAsyncCompositionRoot"/> to register from.</typeparam>
/// <returns>A Task indicating registration has completed.</returns>
Task RegisterFromAsync<TAsyncCompositionRoot>()
where TAsyncCompositionRoot : IAsyncCompositionRoot, new();

/// <summary>
/// Registers a factory delegate to be used when resolving a constructor dependency for
/// a implicitly registered service.
Expand Down Expand Up @@ -777,6 +786,19 @@ public interface ICompositionRoot
void Compose(IServiceRegistry serviceRegistry);
}

/// <summary>
/// Represents a class that acts as an asynchronous composition root for an <see cref="IServiceRegistry"/> instance.
/// </summary>
public interface IAsyncCompositionRoot
{
/// <summary>
/// Composes services by adding services to the <paramref name="serviceRegistry"/>.
/// </summary>
/// <param name="serviceRegistry">The target <see cref="IServiceRegistry"/>.</param>
/// /// <returns>A Task indicating composition has completed.</returns>
Task ComposeAsync(IServiceRegistry serviceRegistry);
}

/// <summary>
/// Represents a class that extracts a set of types from an <see cref="Assembly"/>.
/// </summary>
Expand Down Expand Up @@ -954,6 +976,13 @@ public interface ICompositionRootExecutor
/// </summary>
/// <param name="compositionRootType">The concrete <see cref="ICompositionRoot"/> type to be instantiated and executed.</param>
void Execute(Type compositionRootType);

/// <summary>
/// Creates an instance of the <paramref name="asyncCompositionRootType"/> and executes the <see cref="IAsyncCompositionRoot.ComposeAsync"/> method.
/// </summary>
/// <param name="asyncCompositionRootType">The concrete <see cref="IAsyncCompositionRoot"/> type to be instantiated and executed.</param>
/// <returns>A task indicating the IAsyncCompositionRoot execution has completed.</returns>
Task ExecuteAsync(Type asyncCompositionRootType);
}

/// <summary>
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -1920,6 +1949,17 @@ public void RegisterFrom<TCompositionRoot>()
CompositionRootExecutor.Execute(typeof(TCompositionRoot));
}

/// <summary>
/// Asynchronously registers services from the given <typeparamref name="TAsyncCompositionRoot"/> type.
/// </summary>
/// <typeparam name="TAsyncCompositionRoot">The type of <see cref="IAsyncCompositionRoot"/> to register from.</typeparam>
/// <returns>A Task indicating the composition has completed.</returns>
public Task RegisterFromAsync<TAsyncCompositionRoot>()
where TAsyncCompositionRoot : IAsyncCompositionRoot, new()
{
return CompositionRootExecutor.ExecuteAsync(typeof(TAsyncCompositionRoot));
}

/// <summary>
/// Registers a factory delegate to be used when resolving a constructor dependency for
/// a implicitly registered service.
Expand Down Expand Up @@ -5984,6 +6024,7 @@ public class CompositionRootExecutor : ICompositionRootExecutor
{
private readonly IServiceRegistry serviceRegistry;
private readonly Func<Type, ICompositionRoot> activator;
private readonly Func<Type, IAsyncCompositionRoot> asyncActivator;

private readonly IList<Type> executedCompositionRoots = new List<Type>();

Expand All @@ -5994,10 +6035,12 @@ public class CompositionRootExecutor : ICompositionRootExecutor
/// </summary>
/// <param name="serviceRegistry">The <see cref="IServiceRegistry"/> to be configured by the <see cref="ICompositionRoot"/>.</param>
/// <param name="activator">The function delegate that is responsible for creating an instance of the <see cref="ICompositionRoot"/>.</param>
public CompositionRootExecutor(IServiceRegistry serviceRegistry, Func<Type, ICompositionRoot> activator)
/// <param name="asyncActivator">The function delegate that is responsible for creating an instance of the <see cref="IAsyncCompositionRoot"/>.</param>
public CompositionRootExecutor(IServiceRegistry serviceRegistry, Func<Type, ICompositionRoot> activator, Func<Type, IAsyncCompositionRoot> asyncActivator)
{
this.serviceRegistry = serviceRegistry;
this.activator = activator;
this.asyncActivator = asyncActivator;
}

/// <summary>
Expand All @@ -6019,6 +6062,34 @@ public void Execute(Type compositionRootType)
}
}
}

/// <summary>
/// Creates an instance of the <paramref name="asyncCompositionRootType"/> and executes the <see cref="IAsyncCompositionRoot.ComposeAsync"/> method.
/// </summary>
/// <param name="asyncCompositionRootType">The concrete <see cref="ICompositionRoot"/> type to be instantiated and executed.</param>
/// <returns>A task indicating composition has completed.</returns>
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);
}
}
}
}

/// <summary>
Expand Down