- don't allow resolve of type if a factory is registered for it

pull/59/head
Simon G 3 years ago
parent 7755f3df3d
commit 277fb60873
  1. 26
      LightweightIocContainer/Exceptions/DirectResolveWithRegisteredFactoryNotAllowed.cs
  2. 12
      LightweightIocContainer/Factories/TypedFactory.cs
  3. 28
      LightweightIocContainer/IocContainer.cs
  4. 37
      LightweightIocContainer/LightweightIocContainer.xml
  5. 6
      LightweightIocContainer/Validation/IocValidator.cs
  6. 4
      Test.LightweightIocContainer/FluentFactoryRegistrationTest.cs
  7. 7
      Test.LightweightIocContainer/MultiLayerResolveTest.cs

@ -0,0 +1,26 @@
// Author: Gockner, Simon
// Created: 2022-08-31
// Copyright(c) 2022 SimonG. All Rights Reserved.
using System;
namespace LightweightIocContainer.Exceptions;
/// <summary>
/// A direct resolve with a registered factory is not allowed
/// </summary>
public class DirectResolveWithRegisteredFactoryNotAllowed : IocContainerException
{
/// <summary>
/// A direct resolve with a registered factory is not allowed
/// </summary>
/// <param name="type">The type that can't be resolved directly</param>
public DirectResolveWithRegisteredFactoryNotAllowed(Type type)
: base($"A direct resolve of type {type} is not allowed! Use the registered factory!") =>
Type = type;
/// <summary>
/// The <see cref="Type"/> that can't be resolved directly
/// </summary>
public Type Type { get; }
}

@ -24,7 +24,7 @@ namespace LightweightIocContainer.Factories
/// The
/// </summary>
/// <param name="container">The current instance of the <see cref="IIocContainer"/></param>
public TypedFactory(IIocContainer container) => Factory = CreateFactory(container);
public TypedFactory(IocContainer container) => Factory = CreateFactory(container);
/// <summary>
/// The implemented abstract typed factory/>
@ -36,7 +36,7 @@ namespace LightweightIocContainer.Factories
/// </summary>
/// <exception cref="InvalidFactoryRegistrationException">Factory registration is invalid</exception>
/// <exception cref="IllegalAbstractMethodCreationException">Creation of abstract methods are illegal in their current state</exception>
private TFactory CreateFactory(IIocContainer container)
private TFactory CreateFactory(IocContainer container)
{
Type factoryType = typeof(TFactory);
@ -47,10 +47,10 @@ namespace LightweightIocContainer.Factories
typeBuilder.AddInterfaceImplementation(factoryType);
//add `private readonly IIocContainer _container` field
FieldBuilder containerFieldBuilder = typeBuilder.DefineField("_container", typeof(IIocContainer), FieldAttributes.Private | FieldAttributes.InitOnly);
FieldBuilder containerFieldBuilder = typeBuilder.DefineField("_container", typeof(IocContainer), FieldAttributes.Private | FieldAttributes.InitOnly);
//add ctor
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new[] {typeof(IIocContainer)});
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new[] {typeof(IocContainer)});
ILGenerator constructorGenerator = constructorBuilder.GetILGenerator();
constructorGenerator.Emit(OpCodes.Ldarg_0);
constructorGenerator.Emit(OpCodes.Ldarg_1);
@ -96,7 +96,7 @@ namespace LightweightIocContainer.Factories
generator.EmitCall(OpCodes.Call, emptyArray, null);
}
generator.EmitCall(OpCodes.Callvirt, typeof(IIocResolver).GetMethod(nameof(IIocResolver.Resolve), new[] { typeof(object[]) })!.MakeGenericMethod(createMethod.ReturnType), null);
generator.EmitCall(OpCodes.Call, typeof(IocContainer).GetMethod(nameof(IocContainer.FactoryResolve), new[] { typeof(object[]) })!.MakeGenericMethod(createMethod.ReturnType), null);
generator.Emit(OpCodes.Castclass, createMethod.ReturnType);
generator.Emit(OpCodes.Ret);
}
@ -127,7 +127,7 @@ namespace LightweightIocContainer.Factories
multitonClearGenerator.Emit(OpCodes.Ldarg_0);
multitonClearGenerator.Emit(OpCodes.Ldfld, containerFieldBuilder);
multitonClearGenerator.EmitCall(OpCodes.Callvirt, typeof(IIocContainer).GetMethod(nameof(IIocContainer.ClearMultitonInstances))!.MakeGenericMethod(typeToClear), null);
multitonClearGenerator.EmitCall(OpCodes.Call, typeof(IocContainer).GetMethod(nameof(IocContainer.ClearMultitonInstances))!.MakeGenericMethod(typeToClear), null);
multitonClearGenerator.Emit(OpCodes.Ret);
}
else

@ -113,20 +113,30 @@ namespace LightweightIocContainer
/// <returns>An instance of the given <see cref="Type"/></returns>
public T Resolve<T>(params object[] arguments) => ResolveInternal<T>(arguments);
/// <summary>
/// Gets an instance of the given <see cref="Type"/> for a factory
/// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <param name="arguments">The constructor arguments</param>
/// <returns>An instance of the given <see cref="Type"/></returns>
public T FactoryResolve<T>(params object[] arguments) => ResolveInternal<T>(arguments, null, true);
/// <summary>
/// Gets an instance of a given registered <see cref="Type"/>
/// </summary>
/// <typeparam name="T">The registered <see cref="Type"/></typeparam>
/// <param name="arguments">The constructor arguments</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <param name="isFactoryResolve">True if resolve is called from factory, false (default) if not</param>
/// <returns>An instance of the given registered <see cref="Type"/></returns>
private T ResolveInternal<T>(object[]? arguments, List<Type>? resolveStack = null) => ResolveInstance<T>(TryResolve<T>(arguments, resolveStack));
private T ResolveInternal<T>(object[]? arguments, List<Type>? resolveStack = null, bool isFactoryResolve = false) => ResolveInstance<T>(TryResolve<T>(arguments, resolveStack, isFactoryResolve));
/// <summary>
/// Tries to resolve the given <see cref="Type"/> with the given arguments
/// </summary>
/// <param name="arguments">The given arguments</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <param name="isFactoryResolve">True if resolve is called from factory, false (default) if not</param>
/// <typeparam name="T">The registered <see cref="Type"/></typeparam>
/// <returns>An instance of the given registered <see cref="Type"/>, an <see cref="InternalToBeResolvedPlaceholder"/> if parameters need to be resolved or an <see cref="InternalFactoryMethodPlaceholder{T}"/> if a factory method is used to create an instance</returns>
/// <exception cref="TypeNotRegisteredException">The given <see cref="Type"/> is not registered</exception>
@ -134,7 +144,7 @@ namespace LightweightIocContainer
/// <exception cref="MultitonResolveException">Tried resolving a multiton without scope argument</exception>
/// <exception cref="NoMatchingConstructorFoundException">No matching constructor for the given <see cref="Type"/> found</exception>
/// <exception cref="InternalResolveException">Getting resolve stack failed without exception</exception>
private object TryResolve<T>(object?[]? arguments, List<Type>? resolveStack)
private object TryResolve<T>(object?[]? arguments, List<Type>? resolveStack, bool isFactoryResolve = false)
{
IRegistration registration = FindRegistration<T>() ?? throw new TypeNotRegisteredException(typeof(T));
@ -145,12 +155,13 @@ namespace LightweightIocContainer
if (existingInstance != null)
return existingInstance;
if (registration is ISingleTypeRegistration<T> singleTypeRegistration)
switch (registration)
{
if (singleTypeRegistration.InterfaceType.IsInterface && singleTypeRegistration.FactoryMethod == null)
case IWithFactoryInternal { Factory: { } } when !isFactoryResolve:
throw new DirectResolveWithRegisteredFactoryNotAllowed(typeof(T));
case ISingleTypeRegistration<T> singleTypeRegistration when singleTypeRegistration.InterfaceType.IsInterface && singleTypeRegistration.FactoryMethod == null:
throw new InvalidRegistrationException($"Can't register an interface without its implementation type or without a factory method (Type: {singleTypeRegistration.InterfaceType}).");
if (singleTypeRegistration.FactoryMethod != null)
case ISingleTypeRegistration<T> { FactoryMethod: { } } singleTypeRegistration:
return new InternalFactoryMethodPlaceholder<T>(singleTypeRegistration);
}
@ -187,14 +198,15 @@ namespace LightweightIocContainer
/// <param name="type">The registered <see cref="Type"/></param>
/// <param name="arguments">The given arguments</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <param name="isFactoryResolve"></param>
/// <returns>An instance of the given registered <see cref="Type"/>, an <see cref="InternalToBeResolvedPlaceholder"/> if parameters need to be resolved or an <see cref="InternalFactoryMethodPlaceholder{T}"/> if a factory method is used to create an instance</returns>
/// <exception cref="TypeNotRegisteredException">The given <see cref="Type"/> is not registered</exception>
/// <exception cref="InvalidRegistrationException">An interface was registered without an implementation or factory method</exception>
/// <exception cref="MultitonResolveException">Tried resolving a multiton without scope argument</exception>
/// <exception cref="NoMatchingConstructorFoundException">No matching constructor for the given <see cref="Type"/> found</exception>
/// <exception cref="InternalResolveException">Getting resolve stack failed without exception</exception>
internal object? TryResolveNonGeneric(Type type, object?[]? arguments, List<Type>? resolveStack) =>
GenericMethodCaller.CallPrivate(this, nameof(TryResolve), type, arguments, resolveStack);
internal object? TryResolveNonGeneric(Type type, object?[]? arguments, List<Type>? resolveStack, bool isFactoryResolve = false) =>
GenericMethodCaller.CallPrivate(this, nameof(TryResolve), type, arguments, resolveStack, isFactoryResolve);
/// <summary>
/// Recursively resolve a <see cref="Type"/> with the given parameters for an <see cref="InternalToBeResolvedPlaceholder"/>

@ -130,6 +130,22 @@
The constructor that does not match
</summary>
</member>
<member name="T:LightweightIocContainer.Exceptions.DirectResolveWithRegisteredFactoryNotAllowed">
<summary>
A direct resolve with a registered factory is not allowed
</summary>
</member>
<member name="M:LightweightIocContainer.Exceptions.DirectResolveWithRegisteredFactoryNotAllowed.#ctor(System.Type)">
<summary>
A direct resolve with a registered factory is not allowed
</summary>
<param name="type">The type that can't be resolved directly</param>
</member>
<member name="P:LightweightIocContainer.Exceptions.DirectResolveWithRegisteredFactoryNotAllowed.Type">
<summary>
The <see cref="P:LightweightIocContainer.Exceptions.DirectResolveWithRegisteredFactoryNotAllowed.Type"/> that can't be resolved directly
</summary>
</member>
<member name="T:LightweightIocContainer.Exceptions.GenericMethodNotFoundException">
<summary>
Could not find generic method
@ -341,7 +357,7 @@
</summary>
<typeparam name="TFactory">The type of the abstract factory</typeparam>
</member>
<member name="M:LightweightIocContainer.Factories.TypedFactory`1.#ctor(LightweightIocContainer.Interfaces.IIocContainer)">
<member name="M:LightweightIocContainer.Factories.TypedFactory`1.#ctor(LightweightIocContainer.IocContainer)">
<summary>
The
</summary>
@ -352,7 +368,7 @@
The implemented abstract typed factory/>
</summary>
</member>
<member name="M:LightweightIocContainer.Factories.TypedFactory`1.CreateFactory(LightweightIocContainer.Interfaces.IIocContainer)">
<member name="M:LightweightIocContainer.Factories.TypedFactory`1.CreateFactory(LightweightIocContainer.IocContainer)">
<summary>
Creates the factory from the given abstract factory type
</summary>
@ -935,21 +951,31 @@
<param name="arguments">The constructor arguments</param>
<returns>An instance of the given <see cref="T:System.Type"/></returns>
</member>
<member name="M:LightweightIocContainer.IocContainer.ResolveInternal``1(System.Object[],System.Collections.Generic.List{System.Type})">
<member name="M:LightweightIocContainer.IocContainer.FactoryResolve``1(System.Object[])">
<summary>
Gets an instance of the given <see cref="T:System.Type"/> for a factory
</summary>
<typeparam name="T">The given <see cref="T:System.Type"/></typeparam>
<param name="arguments">The constructor arguments</param>
<returns>An instance of the given <see cref="T:System.Type"/></returns>
</member>
<member name="M:LightweightIocContainer.IocContainer.ResolveInternal``1(System.Object[],System.Collections.Generic.List{System.Type},System.Boolean)">
<summary>
Gets an instance of a given registered <see cref="T:System.Type"/>
</summary>
<typeparam name="T">The registered <see cref="T:System.Type"/></typeparam>
<param name="arguments">The constructor arguments</param>
<param name="resolveStack">The current resolve stack</param>
<param name="isFactoryResolve">True if resolve is called from factory, false (default) if not</param>
<returns>An instance of the given registered <see cref="T:System.Type"/></returns>
</member>
<member name="M:LightweightIocContainer.IocContainer.TryResolve``1(System.Object[],System.Collections.Generic.List{System.Type})">
<member name="M:LightweightIocContainer.IocContainer.TryResolve``1(System.Object[],System.Collections.Generic.List{System.Type},System.Boolean)">
<summary>
Tries to resolve the given <see cref="T:System.Type"/> with the given arguments
</summary>
<param name="arguments">The given arguments</param>
<param name="resolveStack">The current resolve stack</param>
<param name="isFactoryResolve">True if resolve is called from factory, false (default) if not</param>
<typeparam name="T">The registered <see cref="T:System.Type"/></typeparam>
<returns>An instance of the given registered <see cref="T:System.Type"/>, an <see cref="T:LightweightIocContainer.ResolvePlaceholders.InternalToBeResolvedPlaceholder"/> if parameters need to be resolved or an <see cref="T:LightweightIocContainer.ResolvePlaceholders.InternalFactoryMethodPlaceholder`1"/> if a factory method is used to create an instance</returns>
<exception cref="T:LightweightIocContainer.Exceptions.TypeNotRegisteredException">The given <see cref="T:System.Type"/> is not registered</exception>
@ -958,13 +984,14 @@
<exception cref="T:LightweightIocContainer.Exceptions.NoMatchingConstructorFoundException">No matching constructor for the given <see cref="T:System.Type"/> found</exception>
<exception cref="T:LightweightIocContainer.Exceptions.InternalResolveException">Getting resolve stack failed without exception</exception>
</member>
<member name="M:LightweightIocContainer.IocContainer.TryResolveNonGeneric(System.Type,System.Object[],System.Collections.Generic.List{System.Type})">
<member name="M:LightweightIocContainer.IocContainer.TryResolveNonGeneric(System.Type,System.Object[],System.Collections.Generic.List{System.Type},System.Boolean)">
<summary>
Tries to resolve the given <see cref="T:System.Type"/> with the given arguments without generic arguments
</summary>
<param name="type">The registered <see cref="T:System.Type"/></param>
<param name="arguments">The given arguments</param>
<param name="resolveStack">The current resolve stack</param>
<param name="isFactoryResolve"></param>
<returns>An instance of the given registered <see cref="T:System.Type"/>, an <see cref="T:LightweightIocContainer.ResolvePlaceholders.InternalToBeResolvedPlaceholder"/> if parameters need to be resolved or an <see cref="T:LightweightIocContainer.ResolvePlaceholders.InternalFactoryMethodPlaceholder`1"/> if a factory method is used to create an instance</returns>
<exception cref="T:LightweightIocContainer.Exceptions.TypeNotRegisteredException">The given <see cref="T:System.Type"/> is not registered</exception>
<exception cref="T:LightweightIocContainer.Exceptions.InvalidRegistrationException">An interface was registered without an implementation or factory method</exception>

@ -58,7 +58,7 @@ namespace LightweightIocContainer.Validation
.FirstOrDefault(p => parameterType.IsInstanceOfType(p.parameter))
select definedParameter == default ? GetMockOrDefault(parameterType) : definedParameter.parameter).ToArray())
.ToList()
.ForEach(p => TryResolve(registration.InterfaceType, p, validationExceptions));
.ForEach(p => TryResolve(registration.InterfaceType, p, validationExceptions, true));
}
else
TryResolve(registration.InterfaceType, null, validationExceptions);
@ -68,11 +68,11 @@ namespace LightweightIocContainer.Validation
throw new AggregateException("Validation failed.", validationExceptions);
}
private void TryResolve(Type type, object?[]? arguments, List<Exception> validationExceptions)
private void TryResolve(Type type, object?[]? arguments, List<Exception> validationExceptions, bool isFactoryResolve = false)
{
try
{
_iocContainer.TryResolveNonGeneric(type, arguments, null);
_iocContainer.TryResolveNonGeneric(type, arguments, null, isFactoryResolve);
}
catch (Exception exception)
{

@ -112,7 +112,7 @@ namespace Test.LightweightIocContainer
_iocContainer.Register(r => r.Add<ITest, Test>().WithFactory<ITestFactory>());
ITestFactory factory = _iocContainer.Resolve<ITestFactory>();
ITest test = _iocContainer.Resolve<ITest>();
ITest test = factory.Create();
Assert.IsInstanceOf<ITestFactory>(factory);
Assert.IsInstanceOf<ITest>(test);
@ -124,7 +124,7 @@ namespace Test.LightweightIocContainer
_iocContainer.Register(r => r.Add<ITest, Test>().WithFactory<ITestFactory, TestFactory>());
ITestFactory factory = _iocContainer.Resolve<ITestFactory>();
ITest test = _iocContainer.Resolve<ITest>();
ITest test = factory.Create();
Assert.IsInstanceOf<ITestFactory>(factory);
Assert.IsInstanceOf<ITest>(test);

@ -30,6 +30,7 @@ public class MultiLayerResolveTest
[UsedImplicitly]
public interface IBFactory
{
IB Create();
IB Create(C c);
}
@ -76,7 +77,8 @@ public class MultiLayerResolveTest
container.Register(r => r.Add<IA, A>().WithFactory<IAFactory>());
container.Register(r => r.Add<IB, B>().WithFactory<IBFactory>());
IA a = container.Resolve<IA>();
IAFactory aFactory = container.Resolve<IAFactory>();
IA a = aFactory.Create();
Assert.IsInstanceOf<A>(a);
}
@ -88,7 +90,8 @@ public class MultiLayerResolveTest
container.Register(r => r.Add<IB, B>().WithFactory<IBFactory>());
container.Register(r => r.Add<C>().WithFactoryMethod(_ => new C("test")));
IB b = container.Resolve<IB>();
IBFactory bFactory = container.Resolve<IBFactory>();
IB b = bFactory.Create();
Assert.IsInstanceOf<B>(b);
}

Loading…
Cancel
Save