WIP: Support 'And' to bind multiple services to type

This commit is contained in:
Antony Male 2015-09-28 08:35:20 +01:00
parent 447f68f1f0
commit 498597c0a6
9 changed files with 102 additions and 53 deletions

View File

@ -0,0 +1,15 @@
using System;
namespace StyletIoC.Creation
{
public class BuilderTypeKey
{
public Type Type { get; set; }
public string Key { get; set; }
public BuilderTypeKey(Type type)
{
this.Type = type;
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace StyletIoC.Creation
@ -7,11 +8,10 @@ namespace StyletIoC.Creation
/// Delegate used to create an IRegistration
/// </summary>
/// <param name="parentContext">Context on which this registration will be created</param>
/// <param name="serviceType">Service type for this registration</param>
/// <param name="serviceTypes">Service types and keys for this registration</param>
/// <param name="creator">ICreator used by the IRegistration to create new instances</param>
/// <param name="key">Key associated with the registration</param>
/// <returns>A new IRegistration</returns>
public delegate IRegistration RegistrationFactory(IRegistrationContext parentContext, Type serviceType, ICreator creator, string key);
public delegate IRegistration RegistrationFactory(IRegistrationContext parentContext, List<BuilderTypeKey> serviceTypes, ICreator creator);
/// <summary>
/// An IRegistration is responsible to returning an appropriate (new or cached) instanced of a type, or an expression doing the same.

View File

@ -1,25 +1,33 @@
using StyletIoC.Internal.Creators;
using StyletIoC.Internal.Registrations;
using System;
using StyletIoC.Creation;
using System.Collections.Generic;
namespace StyletIoC.Internal.Builders
{
internal class BuilderAbstractFactoryBinding : BuilderBindingBase
{
public BuilderAbstractFactoryBinding(Type serviceType)
: base(serviceType)
public BuilderAbstractFactoryBinding(List<BuilderTypeKey> serviceTypes)
: base(serviceTypes)
{
if (serviceType.IsGenericTypeDefinition)
throw new StyletIoCRegistrationException(String.Format("Unbound generic type {0} can't be used as an abstract factory", serviceType.GetDescription()));
foreach (var serviceType in this.ServiceTypes)
{
if (serviceType.Type.IsGenericTypeDefinition)
throw new StyletIoCRegistrationException(String.Format("Unbound generic type {0} can't be used as an abstract factory", serviceType.Type.GetDescription()));
}
}
public override void Build(Container container)
{
var factoryType = container.GetFactoryForType(this.ServiceType);
var creator = new AbstractFactoryCreator(factoryType);
var registration = new TransientRegistration(creator);
foreach (var serviceType in this.ServiceTypes)
{
var factoryType = container.GetFactoryForType(serviceType.Type);
var creator = new AbstractFactoryCreator(factoryType);
var registration = new TransientRegistration(creator);
container.AddRegistration(new TypeKey(this.ServiceType.TypeHandle, this.Key), registration);
container.AddRegistration(new TypeKey(serviceType.Type.TypeHandle, serviceType.Key), registration);
}
}
}
}

View File

@ -9,25 +9,24 @@ namespace StyletIoC.Internal.Builders
internal class BuilderBindTo : IBindTo
{
private readonly Func<IEnumerable<Assembly>, string, IEnumerable<Assembly>> getAssemblies;
public Type ServiceType { get; private set; }
public List<BuilderTypeKey> ServiceTypes { get; private set; }
private BuilderBindingBase builderBinding;
public bool IsWeak { get { return this.builderBinding.IsWeak; } }
public string Key { get { return this.builderBinding.Key; } }
public BuilderBindTo(Type serviceType, Func<IEnumerable<Assembly>, string, IEnumerable<Assembly>> getAssemblies)
{
this.ServiceType = serviceType;
this.ServiceTypes = new List<BuilderTypeKey>() { new BuilderTypeKey(serviceType) };
this.getAssemblies = getAssemblies;
}
public IInScopeOrWithKeyOrAsWeakBinding ToSelf()
{
return this.To(this.ServiceType);
return this.To(this.ServiceTypes);
}
public IInScopeOrWithKeyOrAsWeakBinding To(Type implementationType)
{
this.builderBinding = new BuilderTypeBinding(this.ServiceType, implementationType);
this.builderBinding = new BuilderTypeBinding(this.ServiceTypes, implementationType);
return this.builderBinding;
}
@ -38,25 +37,25 @@ namespace StyletIoC.Internal.Builders
public IInScopeOrWithKeyOrAsWeakBinding ToFactory<TImplementation>(Func<IRegistrationContext, TImplementation> factory)
{
this.builderBinding = new BuilderFactoryBinding<TImplementation>(this.ServiceType, factory);
this.builderBinding = new BuilderFactoryBinding<TImplementation>(this.ServiceTypes, factory);
return this.builderBinding;
}
public IWithKeyOrAsWeakBinding ToInstance(object instance)
{
this.builderBinding = new BuilderInstanceBinding(this.ServiceType, instance);
this.builderBinding = new BuilderInstanceBinding(this.ServiceTypes, instance);
return this.builderBinding;
}
public IWithKeyOrAsWeakBinding ToAbstractFactory()
{
this.builderBinding = new BuilderAbstractFactoryBinding(this.ServiceType);
this.builderBinding = new BuilderAbstractFactoryBinding(this.ServiceTypes);
return this.builderBinding;
}
public IInScopeOrWithKeyOrAsWeakBinding ToAllImplementations(IEnumerable<Assembly> assemblies)
{
this.builderBinding = new BuilderToAllImplementationsBinding(this.ServiceType, this.getAssemblies(assemblies, "ToAllImplementations"));
this.builderBinding = new BuilderToAllImplementationsBinding(this.ServiceTypes, this.getAssemblies(assemblies, "ToAllImplementations"));
return this.builderBinding;
}

View File

@ -3,22 +3,22 @@ using StyletIoC.Internal.Creators;
using StyletIoC.Internal.Registrations;
using System;
using System.Reflection;
using System.Collections.Generic;
namespace StyletIoC.Internal.Builders
{
internal abstract class BuilderBindingBase : IInScopeOrWithKeyOrAsWeakBinding, IWithKeyOrAsWeakBinding
{
protected Type ServiceType { get; set; }
protected List<BuilderTypeKey> ServiceTypes { get; private set; }
protected RegistrationFactory RegistrationFactory { get; set; }
public string Key { get; protected set; }
public bool IsWeak { get; protected set; }
protected BuilderBindingBase(Type serviceType)
protected BuilderBindingBase(List<BuilderTypeKey> serviceTypes)
{
this.ServiceType = serviceType;
this.ServiceTypes = serviceTypes;
// Default is transient
this.RegistrationFactory = (ctx, service, creator, key) => new TransientRegistration(creator);
this.RegistrationFactory = (ctx, services, creator) => new TransientRegistration(creator);
}
public IAsWeakBinding WithRegistrationFactory(RegistrationFactory registrationFactory)
@ -35,19 +35,25 @@ namespace StyletIoC.Internal.Builders
/// <returns>Fluent interface to continue configuration</returns>
public IAsWeakBinding InSingletonScope()
{
return this.WithRegistrationFactory((ctx, serviceType, creator, key) => new SingletonRegistration(ctx, creator));
return this.WithRegistrationFactory((ctx, serviceTypes, creator) => new SingletonRegistration(ctx, creator));
}
public IInScopeOrAsWeakBinding WithKey(string key)
{
this.Key = key;
this.ServiceTypes[this.ServiceTypes.Count - 1].Key = key;
return this;
}
protected void EnsureType(Type implementationType, Type serviceType = null, bool assertImplementation = true)
protected void EnsureTypeAgainstServiceTypes(Type implementationType, bool assertImplementation = true)
{
serviceType = serviceType ?? this.ServiceType;
foreach (var serviceType in this.ServiceTypes)
{
EnsureType(implementationType, serviceType.Type, assertImplementation);
}
}
protected static void EnsureType(Type implementationType, Type serviceType, bool assertImplementation = true)
{
if (assertImplementation && (!implementationType.IsClass || implementationType.IsAbstract))
throw new StyletIoCRegistrationException(String.Format("Type {0} is not a concrete class, and so can't be used to implemented service {1}", implementationType.GetDescription(), serviceType.GetDescription()));
@ -73,34 +79,40 @@ namespace StyletIoC.Internal.Builders
throw new StyletIoCRegistrationException(String.Format("Type {0} does not implement service {1}", implementationType.GetDescription(), serviceType.GetDescription()));
}
// Convenience...
protected void BindImplementationToService(Container container, Type implementationType, Type serviceType = null)
protected void BindImplementationToServices(Container container, Type implementationType)
{
serviceType = serviceType ?? this.ServiceType;
foreach (var serviceType in this.ServiceTypes)
{
this.BindImplementationToSpecificService(container, implementationType, serviceType.Type, serviceType.Key);
}
}
// Convenience...
protected void BindImplementationToSpecificService(Container container, Type implementationType, Type serviceType, string key)
{
if (serviceType.IsGenericTypeDefinition)
{
var unboundGeneric = new UnboundGeneric(serviceType, implementationType, container, this.RegistrationFactory);
container.AddUnboundGeneric(new TypeKey(serviceType.TypeHandle, this.Key), unboundGeneric);
container.AddUnboundGeneric(new TypeKey(serviceType.TypeHandle, key), unboundGeneric);
}
else
{
var creator = new TypeCreator(implementationType, container);
var registration = this.CreateRegistration(container, creator);
container.AddRegistration(new TypeKey(serviceType.TypeHandle, this.Key ?? creator.AttributeKey), registration);
container.AddRegistration(new TypeKey(serviceType.TypeHandle, key ?? creator.AttributeKey), registration);
}
}
// Convenience...
protected IRegistration CreateRegistration(IRegistrationContext registrationContext, ICreator creator)
{
return this.RegistrationFactory(registrationContext, this.ServiceType, creator, this.Key);
return this.RegistrationFactory(registrationContext, this.ServiceTypes, creator);
}
IAsWeakBinding IWithKeyOrAsWeakBinding.WithKey(string key)
{
this.Key = key;
this.ServiceTypes[this.ServiceTypes.Count - 1].Key = key;
return this;
}

View File

@ -1,6 +1,7 @@
using StyletIoC.Creation;
using StyletIoC.Internal.Creators;
using System;
using System.Collections.Generic;
namespace StyletIoC.Internal.Builders
{
@ -8,12 +9,15 @@ namespace StyletIoC.Internal.Builders
{
private readonly Func<IRegistrationContext, TImplementation> factory;
public BuilderFactoryBinding(Type serviceType, Func<IRegistrationContext, TImplementation> factory)
: base(serviceType)
public BuilderFactoryBinding(List<BuilderTypeKey> serviceTypes, Func<IRegistrationContext, TImplementation> factory)
: base(serviceTypes)
{
if (this.ServiceType.IsGenericTypeDefinition)
throw new StyletIoCRegistrationException(String.Format("A factory cannot be used to implement unbound generic type {0}", this.ServiceType.GetDescription()));
this.EnsureType(typeof(TImplementation), assertImplementation: false);
foreach (var serviceType in this.ServiceTypes)
{
if (serviceType.Type.IsGenericTypeDefinition)
throw new StyletIoCRegistrationException(String.Format("A factory cannot be used to implement unbound generic type {0}", serviceType.Type.GetDescription()));
this.EnsureTypeAgainstServiceTypes(typeof(TImplementation), assertImplementation: false);
}
this.factory = factory;
}
@ -22,7 +26,10 @@ namespace StyletIoC.Internal.Builders
var creator = new FactoryCreator<TImplementation>(this.factory, container);
var registration = this.CreateRegistration(container, creator);
container.AddRegistration(new TypeKey(this.ServiceType.TypeHandle, this.Key), registration);
foreach (var serviceType in this.ServiceTypes)
{
container.AddRegistration(new TypeKey(serviceType.Type.TypeHandle, serviceType.Key), registration);
}
}
}
}

View File

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using StyletIoC.Creation;
using StyletIoC.Internal.Creators;
namespace StyletIoC.Internal.Builders
@ -7,10 +9,10 @@ namespace StyletIoC.Internal.Builders
{
private readonly object instance;
public BuilderInstanceBinding(Type serviceType, object instance)
: base(serviceType)
public BuilderInstanceBinding(List<BuilderTypeKey> serviceTypes, object instance)
: base(serviceTypes)
{
this.EnsureType(instance.GetType(), assertImplementation: false);
this.EnsureTypeAgainstServiceTypes(instance.GetType(), assertImplementation: false);
this.instance = instance;
}
@ -19,7 +21,10 @@ namespace StyletIoC.Internal.Builders
var creator = new InstanceCreator(this.instance);
var registration = this.CreateRegistration(container, creator);
container.AddRegistration(new TypeKey(this.ServiceType.TypeHandle, this.Key), registration);
foreach (var serviceType in this.ServiceTypes)
{
container.AddRegistration(new TypeKey(serviceType.Type.TypeHandle, serviceType.Key), registration);
}
}
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using StyletIoC.Creation;
namespace StyletIoC.Internal.Builders
{
@ -10,8 +11,8 @@ namespace StyletIoC.Internal.Builders
{
private readonly IEnumerable<Assembly> assemblies;
public BuilderToAllImplementationsBinding(Type serviceType, IEnumerable<Assembly> assemblies)
: base(serviceType)
public BuilderToAllImplementationsBinding(List<BuilderTypeKey> serviceTypes, IEnumerable<Assembly> assemblies)
: base(serviceTypes)
{
this.assemblies = assemblies;
}
@ -19,7 +20,7 @@ namespace StyletIoC.Internal.Builders
public override void Build(Container container)
{
var candidates = from type in this.assemblies.Distinct().SelectMany(x => x.GetTypes())
let baseType = type.GetBaseTypesAndInterfaces().FirstOrDefault(x => x == this.ServiceType || (x.IsGenericType && x.GetGenericTypeDefinition() == this.ServiceType))
let baseType = type.GetBaseTypesAndInterfaces().FirstOrDefault(x => x == this.ServiceTypes || (x.IsGenericType && x.GetGenericTypeDefinition() == this.ServiceTypes))
where baseType != null
select new { Type = type, Base = baseType.ContainsGenericParameters ? baseType.GetGenericTypeDefinition() : baseType };

View File

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using StyletIoC.Creation;
namespace StyletIoC.Internal.Builders
{
@ -6,16 +8,16 @@ namespace StyletIoC.Internal.Builders
{
private readonly Type implementationType;
public BuilderTypeBinding(Type serviceType, Type implementationType)
: base(serviceType)
public BuilderTypeBinding(List<BuilderTypeKey> serviceTypes, Type implementationType)
: base(serviceTypes)
{
this.EnsureType(implementationType);
this.EnsureTypeAgainstServiceTypes(implementationType);
this.implementationType = implementationType;
}
public override void Build(Container container)
{
this.BindImplementationToService(container, this.implementationType);
this.BindImplementationToServices(container, this.implementationType);
}
}
}