diff --git a/LightweightIocContainer/Exceptions/DirectResolveWithRegisteredFactoryNotAllowed.cs b/LightweightIocContainer/Exceptions/DirectResolveWithRegisteredFactoryNotAllowed.cs new file mode 100644 index 0000000..c692655 --- /dev/null +++ b/LightweightIocContainer/Exceptions/DirectResolveWithRegisteredFactoryNotAllowed.cs @@ -0,0 +1,26 @@ +// Author: Gockner, Simon +// Created: 2022-08-31 +// Copyright(c) 2022 SimonG. All Rights Reserved. + +using System; + +namespace LightweightIocContainer.Exceptions; + +/// +/// A direct resolve with a registered factory is not allowed +/// +public class DirectResolveWithRegisteredFactoryNotAllowed : IocContainerException +{ + /// + /// A direct resolve with a registered factory is not allowed + /// + /// The type that can't be resolved directly + public DirectResolveWithRegisteredFactoryNotAllowed(Type type) + : base($"A direct resolve of type {type} is not allowed! Use the registered factory!") => + Type = type; + + /// + /// The that can't be resolved directly + /// + public Type Type { get; } +} \ No newline at end of file diff --git a/LightweightIocContainer/Factories/TypedFactory.cs b/LightweightIocContainer/Factories/TypedFactory.cs index fe7e7f8..ddd0c16 100644 --- a/LightweightIocContainer/Factories/TypedFactory.cs +++ b/LightweightIocContainer/Factories/TypedFactory.cs @@ -24,7 +24,7 @@ namespace LightweightIocContainer.Factories /// The /// /// The current instance of the - public TypedFactory(IIocContainer container) => Factory = CreateFactory(container); + public TypedFactory(IocContainer container) => Factory = CreateFactory(container); /// /// The implemented abstract typed factory/> @@ -36,7 +36,7 @@ namespace LightweightIocContainer.Factories /// /// Factory registration is invalid /// Creation of abstract methods are illegal in their current state - 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 diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index 0fe5c7b..b27627a 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -113,20 +113,30 @@ namespace LightweightIocContainer /// An instance of the given public T Resolve(params object[] arguments) => ResolveInternal(arguments); + /// + /// Gets an instance of the given for a factory + /// + /// The given + /// The constructor arguments + /// An instance of the given + public T FactoryResolve(params object[] arguments) => ResolveInternal(arguments, null, true); + /// /// Gets an instance of a given registered /// /// The registered /// The constructor arguments /// The current resolve stack + /// True if resolve is called from factory, false (default) if not /// An instance of the given registered - private T ResolveInternal(object[]? arguments, List? resolveStack = null) => ResolveInstance(TryResolve(arguments, resolveStack)); + private T ResolveInternal(object[]? arguments, List? resolveStack = null, bool isFactoryResolve = false) => ResolveInstance(TryResolve(arguments, resolveStack, isFactoryResolve)); /// /// Tries to resolve the given with the given arguments /// /// The given arguments /// The current resolve stack + /// True if resolve is called from factory, false (default) if not /// The registered /// An instance of the given registered , an if parameters need to be resolved or an if a factory method is used to create an instance /// The given is not registered @@ -134,7 +144,7 @@ namespace LightweightIocContainer /// Tried resolving a multiton without scope argument /// No matching constructor for the given found /// Getting resolve stack failed without exception - private object TryResolve(object?[]? arguments, List? resolveStack) + private object TryResolve(object?[]? arguments, List? resolveStack, bool isFactoryResolve = false) { IRegistration registration = FindRegistration() ?? throw new TypeNotRegisteredException(typeof(T)); @@ -145,12 +155,13 @@ namespace LightweightIocContainer if (existingInstance != null) return existingInstance; - if (registration is ISingleTypeRegistration singleTypeRegistration) + switch (registration) { - if (singleTypeRegistration.InterfaceType.IsInterface && singleTypeRegistration.FactoryMethod == null) + case IWithFactoryInternal { Factory: { } } when !isFactoryResolve: + throw new DirectResolveWithRegisteredFactoryNotAllowed(typeof(T)); + case ISingleTypeRegistration 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 { FactoryMethod: { } } singleTypeRegistration: return new InternalFactoryMethodPlaceholder(singleTypeRegistration); } @@ -187,14 +198,15 @@ namespace LightweightIocContainer /// The registered /// The given arguments /// The current resolve stack + /// /// An instance of the given registered , an if parameters need to be resolved or an if a factory method is used to create an instance /// The given is not registered /// An interface was registered without an implementation or factory method /// Tried resolving a multiton without scope argument /// No matching constructor for the given found /// Getting resolve stack failed without exception - internal object? TryResolveNonGeneric(Type type, object?[]? arguments, List? resolveStack) => - GenericMethodCaller.CallPrivate(this, nameof(TryResolve), type, arguments, resolveStack); + internal object? TryResolveNonGeneric(Type type, object?[]? arguments, List? resolveStack, bool isFactoryResolve = false) => + GenericMethodCaller.CallPrivate(this, nameof(TryResolve), type, arguments, resolveStack, isFactoryResolve); /// /// Recursively resolve a with the given parameters for an diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index 3776897..cde8b71 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -130,6 +130,22 @@ The constructor that does not match + + + A direct resolve with a registered factory is not allowed + + + + + A direct resolve with a registered factory is not allowed + + The type that can't be resolved directly + + + + The that can't be resolved directly + + Could not find generic method @@ -341,7 +357,7 @@ The type of the abstract factory - + The @@ -352,7 +368,7 @@ The implemented abstract typed factory/> - + Creates the factory from the given abstract factory type @@ -935,21 +951,31 @@ The constructor arguments An instance of the given - + + + Gets an instance of the given for a factory + + The given + The constructor arguments + An instance of the given + + Gets an instance of a given registered The registered The constructor arguments The current resolve stack + True if resolve is called from factory, false (default) if not An instance of the given registered - + Tries to resolve the given with the given arguments The given arguments The current resolve stack + True if resolve is called from factory, false (default) if not The registered An instance of the given registered , an if parameters need to be resolved or an if a factory method is used to create an instance The given is not registered @@ -958,13 +984,14 @@ No matching constructor for the given found Getting resolve stack failed without exception - + Tries to resolve the given with the given arguments without generic arguments The registered The given arguments The current resolve stack + An instance of the given registered , an if parameters need to be resolved or an if a factory method is used to create an instance The given is not registered An interface was registered without an implementation or factory method diff --git a/LightweightIocContainer/Validation/IocValidator.cs b/LightweightIocContainer/Validation/IocValidator.cs index 231e801..e2a45c6 100644 --- a/LightweightIocContainer/Validation/IocValidator.cs +++ b/LightweightIocContainer/Validation/IocValidator.cs @@ -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 validationExceptions) + private void TryResolve(Type type, object?[]? arguments, List validationExceptions, bool isFactoryResolve = false) { try { - _iocContainer.TryResolveNonGeneric(type, arguments, null); + _iocContainer.TryResolveNonGeneric(type, arguments, null, isFactoryResolve); } catch (Exception exception) { diff --git a/Test.LightweightIocContainer/FluentFactoryRegistrationTest.cs b/Test.LightweightIocContainer/FluentFactoryRegistrationTest.cs index b437f41..851388d 100644 --- a/Test.LightweightIocContainer/FluentFactoryRegistrationTest.cs +++ b/Test.LightweightIocContainer/FluentFactoryRegistrationTest.cs @@ -112,7 +112,7 @@ namespace Test.LightweightIocContainer _iocContainer.Register(r => r.Add().WithFactory()); ITestFactory factory = _iocContainer.Resolve(); - ITest test = _iocContainer.Resolve(); + ITest test = factory.Create(); Assert.IsInstanceOf(factory); Assert.IsInstanceOf(test); @@ -124,7 +124,7 @@ namespace Test.LightweightIocContainer _iocContainer.Register(r => r.Add().WithFactory()); ITestFactory factory = _iocContainer.Resolve(); - ITest test = _iocContainer.Resolve(); + ITest test = factory.Create(); Assert.IsInstanceOf(factory); Assert.IsInstanceOf(test); diff --git a/Test.LightweightIocContainer/MultiLayerResolveTest.cs b/Test.LightweightIocContainer/MultiLayerResolveTest.cs index 1e7181c..16505c3 100644 --- a/Test.LightweightIocContainer/MultiLayerResolveTest.cs +++ b/Test.LightweightIocContainer/MultiLayerResolveTest.cs @@ -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().WithFactory()); container.Register(r => r.Add().WithFactory()); - IA a = container.Resolve(); + IAFactory aFactory = container.Resolve(); + IA a = aFactory.Create(); Assert.IsInstanceOf(a); } @@ -88,7 +90,8 @@ public class MultiLayerResolveTest container.Register(r => r.Add().WithFactory()); container.Register(r => r.Add().WithFactoryMethod(_ => new C("test"))); - IB b = container.Resolve(); + IBFactory bFactory = container.Resolve(); + IB b = bFactory.Create(); Assert.IsInstanceOf(b); }