GetAll returns empty enumerable if no registrations found

This commit is contained in:
Antony Male 2014-09-12 14:50:11 +01:00
parent 26116cb528
commit 142fb40ed9
10 changed files with 65 additions and 16 deletions

View File

@ -64,6 +64,7 @@
<Compile Include="StyletIoC\Internal\Creators\TypeCreator.cs" />
<Compile Include="StyletIoC\Internal\DelegatingDictionary.cs" />
<Compile Include="StyletIoC\Creation\IRegistrationContext.cs" />
<Compile Include="StyletIoC\Internal\RegistrationCollections\EmptyRegistrationCollection.cs" />
<Compile Include="StyletIoC\Internal\RegistrationCollections\RegistrationCollection.cs" />
<Compile Include="StyletIoC\Internal\RegistrationCollections\SingleRegistration.cs" />
<Compile Include="StyletIoC\Internal\Registrations\FuncNoKeyRegistration.cs" />

View File

@ -40,7 +40,7 @@ namespace StyletIoC.Creation
IRegistration GetSingleRegistration(Type type, string key, bool searchGetAllTypes);
/// <summary>
/// Retrieve all IRegistrations for the type+key combination, or throw an exception if there are none
/// Retrieve all IRegistrations for the type+key combination
/// </summary>
/// <remarks>If a single registration exists, then the returned list will contain a single entry</remarks>
/// <param name="type">Type to search for</param>

View File

@ -18,7 +18,7 @@ namespace StyletIoC.Internal.Builders
this.serviceType = serviceType;
// Default is transient
this.registrationFactory = (ctx, creator, key) => new TransientRegistration(creator);
this.registrationFactory = (ctx, service, creator, key) => new TransientRegistration(creator);
}
IAsWeakBinding IInScopeOrAsWeakBinding.WithRegistrationFactory(RegistrationFactory registrationFactory)
@ -71,7 +71,7 @@ namespace StyletIoC.Internal.Builders
if (serviceType.IsGenericTypeDefinition)
{
var unboundGeneric = new UnboundGeneric(implementationType, container, this.registrationFactory);
var unboundGeneric = new UnboundGeneric(serviceType, implementationType, container, this.registrationFactory);
container.AddUnboundGeneric(new TypeKey(serviceType, this.Key), unboundGeneric);
}
else
@ -86,7 +86,7 @@ namespace StyletIoC.Internal.Builders
// Convenience...
protected IRegistration CreateRegistration(IRegistrationContext registrationContext, ICreator creator)
{
return this.registrationFactory(registrationContext, creator, this.Key);
return this.registrationFactory(registrationContext, this.serviceType, creator, this.Key);
}
IAsWeakBinding IWithKeyOrAsWeakBinding.WithKey(string key)

View File

@ -180,6 +180,8 @@ namespace StyletIoC.Internal
return true;
}
// Is it a 'get all' request?
IRegistration registration;
return this.TryRetrieveGetAllRegistration(typeKey, out registration);
@ -192,7 +194,7 @@ namespace StyletIoC.Internal
{
Type type = typeKey.Type;
// Elements are never removed from this.registrations, so we're safe to make this ContainsKey query
if (!type.IsGenericType || type.GenericTypeArguments.Length != 1 || !this.CanResolve(new TypeKey(type.GenericTypeArguments[0], typeKey.Key)))
if (!type.IsGenericType || !typeKey.Type.Implements(typeof(IEnumerable<>)))
return null;
return type.GenericTypeArguments[0];
}
@ -379,7 +381,8 @@ namespace StyletIoC.Internal
}
else
{
throw new StyletIoCRegistrationException(String.Format("No registrations found for service {0}.", typeKey.Type.GetDescription()));
// This will throw a StyletIoCRegistrationException is GetSingle is requested
registrations = new EmptyRegistrationCollection(typeKey.Type);
}
}

View File

@ -0,0 +1,39 @@
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StyletIoC.Internal.RegistrationCollections
{
internal class EmptyRegistrationCollection : IRegistrationCollection
{
private readonly Type type;
public EmptyRegistrationCollection(Type type)
{
this.type = type;
}
public IRegistration GetSingle()
{
throw new StyletIoCRegistrationException(String.Format("No registrations found for service {0}.", this.type.GetDescription()));
}
public List<IRegistration> GetAll()
{
return new List<IRegistration>();
}
public IRegistrationCollection AddRegistration(IRegistration registration)
{
return new SingleRegistration(registration);
}
public IRegistrationCollection CloneToContext(IRegistrationContext context)
{
return this;
}
}
}

View File

@ -52,10 +52,11 @@ namespace StyletIoC.Internal.Registrations
if (this.expression != null)
return this.expression;
var list = Expression.New(this.Type);
var init = Expression.ListInit(list, this.parentContext.GetAllRegistrations(this.Type.GenericTypeArguments[0], this.Key, false).Select(x => x.GetInstanceExpression(registrationContext)));
var listNew = Expression.New(this.Type);
var instanceExpressions = this.parentContext.GetAllRegistrations(this.Type.GenericTypeArguments[0], this.Key, false).Select(x => x.GetInstanceExpression(registrationContext));
Expression list = instanceExpressions.Any() ? (Expression)Expression.ListInit(listNew, instanceExpressions) : listNew;
Interlocked.CompareExchange(ref this.expression, init, null);
Interlocked.CompareExchange(ref this.expression, list, null);
return this.expression;
}

View File

@ -13,6 +13,7 @@ namespace StyletIoC.Internal.Registrations
internal class PerContainerRegistration : RegistrationBase
{
private readonly IRegistrationContext parentContext;
private readonly Type serviceType;
private readonly string key;
private readonly object instanceFactoryLock = new object();
private Func<IRegistrationContext, object> instanceFactory;
@ -20,10 +21,11 @@ namespace StyletIoC.Internal.Registrations
private static readonly MethodInfo getMethod = typeof(IContainer).GetMethod("Get", new[] { typeof(Type), typeof(string) });
public PerContainerRegistration(IRegistrationContext parentContext, ICreator creator, string key, Func<IRegistrationContext, object> instanceFactory = null)
public PerContainerRegistration(IRegistrationContext parentContext, Type serviceType, ICreator creator, string key, Func<IRegistrationContext, object> instanceFactory = null)
: base(creator)
{
this.parentContext = parentContext;
this.serviceType = serviceType;
this.key = key;
this.instanceFactory = instanceFactory;
@ -71,7 +73,7 @@ namespace StyletIoC.Internal.Registrations
public override Expression GetInstanceExpression(ParameterExpression registrationContext)
{
// Always synthesize into a method call onto the current context
var call = Expression.Call(registrationContext, getMethod, Expression.Constant(this.Type), Expression.Constant(this.key, typeof(string)));
var call = Expression.Call(registrationContext, getMethod, Expression.Constant(this.serviceType), Expression.Constant(this.key, typeof(string)));
var cast = Expression.Convert(call, this.Type);
return cast;
}
@ -80,7 +82,7 @@ namespace StyletIoC.Internal.Registrations
{
// Ensure the factory's created, and pass it down. This means the work of compiling the creation expression is done once, ever
this.EnsureInstanceFactoryCreated();
return new PerContainerRegistration(context, this.creator, this.key, this.instanceFactory);
return new PerContainerRegistration(context, this.serviceType, this.creator, this.key, this.instanceFactory);
}
}
}

View File

@ -45,6 +45,7 @@ namespace StyletIoC.Internal
public static bool Implements(this Type implementationType, Type serviceType)
{
return serviceType.IsAssignableFrom(implementationType) ||
(implementationType.IsGenericType && serviceType.IsGenericTypeDefinition && serviceType.IsAssignableFrom(implementationType.GetGenericTypeDefinition())) ||
implementationType.GetBaseTypesAndInterfaces().Any(x => x == serviceType || (x.IsGenericType && x.GetGenericTypeDefinition() == serviceType));
}

View File

@ -7,11 +7,13 @@ namespace StyletIoC.Internal
internal class UnboundGeneric
{
private IRegistrationContext parentContext;
private readonly Type serviceType;
public Type Type { get; private set; }
public RegistrationFactory RegistrationFactory { get; private set; }
public UnboundGeneric(Type type, IRegistrationContext parentContext, RegistrationFactory registrationFactory)
public UnboundGeneric(Type serviceType, Type type, IRegistrationContext parentContext, RegistrationFactory registrationFactory)
{
this.serviceType = serviceType;
this.Type = type;
this.parentContext = parentContext;
this.RegistrationFactory = registrationFactory;
@ -19,7 +21,7 @@ namespace StyletIoC.Internal
public IRegistration CreateRegistrationForTypeKey(TypeKey boundTypeKey)
{
return this.RegistrationFactory(this.parentContext, new TypeCreator(boundTypeKey.Type, this.parentContext), boundTypeKey.Key);
return this.RegistrationFactory(this.parentContext, this.serviceType, new TypeCreator(boundTypeKey.Type, this.parentContext), boundTypeKey.Key);
}
}
}

View File

@ -130,11 +130,11 @@ namespace StyletUnitTests
}
[Test]
public void GetAllThrowsIfNoRegistrationsFound()
public void GetAllDoesNotThrowIfNoRegistrationsFound()
{
var builder = new StyletIoCBuilder();
var ioc = builder.BuildContainer();
Assert.Throws<StyletIoCRegistrationException>(() => ioc.GetAll<IC1>());
Assert.DoesNotThrow(() => ioc.GetAll<IC1>());
}
[Test]