From 5f3c9e28328d8860bee971f69110a93a285d1e1d Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Fri, 22 Nov 2019 11:11:49 +0100 Subject: [PATCH] #29: add ISingleTypeRegistration: - add `WithFactoryMethod()` to set a `FactoryMethod` that gets called when an instance of the registered type is created - allow registration of only an interface, if you resolve only an interface without a factoryMethod an exception is thrown --- .../Interfaces/IIocContainer.cs | 2 +- .../Registrations/ISingleTypeRegistration.cs | 27 +++++++++ LightweightIocContainer/IocContainer.cs | 34 +++++++++--- .../LightweightIocContainer.xml | 55 +++++++++++++++++-- .../Registrations/RegistrationFactory.cs | 15 ++--- .../Registrations/SingleTypeRegistration.cs | 44 +++++++++++++++ .../IocContainerTest.cs | 13 +++-- 7 files changed, 161 insertions(+), 29 deletions(-) create mode 100644 LightweightIocContainer/Interfaces/Registrations/ISingleTypeRegistration.cs create mode 100644 LightweightIocContainer/Registrations/SingleTypeRegistration.cs diff --git a/LightweightIocContainer/Interfaces/IIocContainer.cs b/LightweightIocContainer/Interfaces/IIocContainer.cs index 9a667b8..a3162e3 100644 --- a/LightweightIocContainer/Interfaces/IIocContainer.cs +++ b/LightweightIocContainer/Interfaces/IIocContainer.cs @@ -36,7 +36,7 @@ namespace LightweightIocContainer.Interfaces /// The to register /// The for this /// The created - IRegistrationBase Register(Lifestyle lifestyle = Lifestyle.Transient); + ISingleTypeRegistration Register(Lifestyle lifestyle = Lifestyle.Transient); /// /// Register an Interface with a Type that implements it as a multiton diff --git a/LightweightIocContainer/Interfaces/Registrations/ISingleTypeRegistration.cs b/LightweightIocContainer/Interfaces/Registrations/ISingleTypeRegistration.cs new file mode 100644 index 0000000..8c68244 --- /dev/null +++ b/LightweightIocContainer/Interfaces/Registrations/ISingleTypeRegistration.cs @@ -0,0 +1,27 @@ +// Author: Gockner, Simon +// Created: 2019-11-22 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +using System; + +namespace LightweightIocContainer.Interfaces.Registrations +{ + /// + /// The to register either only an interface or only a + /// + /// The of the + public interface ISingleTypeRegistration : IRegistrationBase + { + /// + /// that is invoked instead of creating an instance of this the default way + /// + Func FactoryMethod { get; } + + /// + /// Pass a that will be invoked instead of creating an instance of this the default way + /// + /// The + /// The current instance of this + IRegistrationBase WithFactoryMethod(Func factoryMethod); + } +} \ No newline at end of file diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index 4c9df25..edd6d88 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -73,9 +73,9 @@ namespace LightweightIocContainer /// The to register /// The for this /// The created - public IRegistrationBase Register(Lifestyle lifestyle = Lifestyle.Transient) + public ISingleTypeRegistration Register(Lifestyle lifestyle = Lifestyle.Transient) { - IRegistrationBase registration = _registrationFactory.Register(lifestyle); + ISingleTypeRegistration registration = _registrationFactory.Register(lifestyle); Register(registration); return registration; @@ -213,7 +213,7 @@ namespace LightweightIocContainer { resolvedInstance = unitTestCallbackRegistration.UnitTestResolveCallback.Invoke(arguments); } - else if (registration is IDefaultRegistration defaultRegistration) + else if (registration is IRegistrationBase defaultRegistration) { if (defaultRegistration.Lifestyle == Lifestyle.Singleton) resolvedInstance = GetOrCreateSingletonInstance(defaultRegistration, arguments, resolveStack); @@ -242,7 +242,7 @@ namespace LightweightIocContainer /// The arguments to resolve /// The current resolve stack /// An existing or newly created singleton instance of the given - private T GetOrCreateSingletonInstance(IDefaultRegistration registration, object[] arguments, List resolveStack) + private T GetOrCreateSingletonInstance(IRegistrationBase registration, object[] arguments, List resolveStack) { //if a singleton instance exists return it object instance = _singletons.FirstOrDefault(s => s.type == typeof(T)).instance; @@ -306,10 +306,30 @@ namespace LightweightIocContainer /// The constructor arguments /// The current resolve stack /// A newly created instance of the given - private T CreateInstance(IDefaultRegistration registration, object[] arguments, List resolveStack) + private T CreateInstance(IRegistrationBase registration, object[] arguments, List resolveStack) { - arguments = ResolveConstructorArguments(registration.ImplementationType, arguments, resolveStack); - T instance = (T) Activator.CreateInstance(registration.ImplementationType, arguments); + T instance; + if (registration is IDefaultRegistration defaultRegistration) + { + arguments = ResolveConstructorArguments(defaultRegistration.ImplementationType, arguments, resolveStack); + instance = (T) Activator.CreateInstance(defaultRegistration.ImplementationType, arguments); + } + else if (registration is ISingleTypeRegistration singleTypeRegistration) + { + if (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) //type registration without interface -> just create this type + { + arguments = ResolveConstructorArguments(singleTypeRegistration.InterfaceType, arguments, resolveStack); + instance = (T)Activator.CreateInstance(singleTypeRegistration.InterfaceType, arguments); + } + else //factory method set to create the instance + instance = singleTypeRegistration.FactoryMethod(this); + } + else + throw new UnknownRegistrationException($"There is no registration of type {registration.GetType().Name}."); + registration.OnCreateAction?.Invoke(instance); //TODO: Allow async OnCreateAction? return instance; diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index abd02ce..613bbb7 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -456,6 +456,24 @@ The The current instance of this + + + The to register either only an interface or only a + + The of the + + + + that is invoked instead of creating an instance of this the default way + + + + + Pass a that will be invoked instead of creating an instance of this the default way + + The + The current instance of this + The registration that is used to register an abstract typed factory @@ -579,7 +597,7 @@ The given is not registered in this The registration for the given has an unknown - + Gets or creates a singleton instance of a given @@ -601,7 +619,7 @@ No arguments given Scope argument not given - + Creates an instance of a given @@ -759,11 +777,11 @@ - Register a without an interface and create a + Register a without an interface and create a - The to register - The for this - A new created with the given parameters + The to register + The for this + A new created with the given parameters @@ -781,6 +799,31 @@ The abstract typed factory to register A new created with the given parameters + + + The to register either only an interface or only a + + The of the + + + + The to register either only an interface or only a + + The of the interface or + The of the + + + + that is invoked instead of creating an instance of this the default way + + + + + Pass a that will be invoked instead of creating an instance of this the default way + + The + The current instance of this + The registration that is used to register an abstract typed factory diff --git a/LightweightIocContainer/Registrations/RegistrationFactory.cs b/LightweightIocContainer/Registrations/RegistrationFactory.cs index e424f8b..8249da8 100644 --- a/LightweightIocContainer/Registrations/RegistrationFactory.cs +++ b/LightweightIocContainer/Registrations/RegistrationFactory.cs @@ -35,17 +35,14 @@ namespace LightweightIocContainer.Registrations } /// - /// Register a without an interface and create a + /// Register a without an interface and create a /// - /// The to register - /// The for this - /// A new created with the given parameters - public IRegistrationBase Register(Lifestyle lifestyle) + /// The to register + /// The for this + /// A new created with the given parameters + public ISingleTypeRegistration Register(Lifestyle lifestyle) { - if (typeof(TImplementation).IsInterface) - throw new InvalidRegistrationException("Can't register an interface without its implementation type."); - - return Register(lifestyle); + return new SingleTypeRegistration(typeof(T), lifestyle); } /// diff --git a/LightweightIocContainer/Registrations/SingleTypeRegistration.cs b/LightweightIocContainer/Registrations/SingleTypeRegistration.cs new file mode 100644 index 0000000..5a7a793 --- /dev/null +++ b/LightweightIocContainer/Registrations/SingleTypeRegistration.cs @@ -0,0 +1,44 @@ +// Author: Gockner, Simon +// Created: 2019-11-22 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +using System; +using LightweightIocContainer.Interfaces; +using LightweightIocContainer.Interfaces.Registrations; + +namespace LightweightIocContainer.Registrations +{ + /// + /// The to register either only an interface or only a + /// + /// The of the + public class SingleTypeRegistration : RegistrationBase, ISingleTypeRegistration + { + /// + /// The to register either only an interface or only a + /// + /// The of the interface or + /// The of the + public SingleTypeRegistration(Type interfaceType, Lifestyle lifestyle) + : base(interfaceType, lifestyle) + { + Name = $"{InterfaceType.Name}, Lifestyle: {Lifestyle.ToString()}"; + } + + /// + /// that is invoked instead of creating an instance of this the default way + /// + public Func FactoryMethod { get; private set; } + + /// + /// Pass a that will be invoked instead of creating an instance of this the default way + /// + /// The + /// The current instance of this + public IRegistrationBase WithFactoryMethod(Func factoryMethod) + { + FactoryMethod = factoryMethod; + return this; + } + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer/IocContainerTest.cs b/Test.LightweightIocContainer/IocContainerTest.cs index 1658ab8..ccffa6c 100644 --- a/Test.LightweightIocContainer/IocContainerTest.cs +++ b/Test.LightweightIocContainer/IocContainerTest.cs @@ -166,12 +166,6 @@ namespace Test.LightweightIocContainer Assert.AreEqual(typeof(ITest), exception.Type); } - [Test] - public void TestRegisterInterfaceWithoutImplementation() - { - Assert.Throws(() => _iocContainer.Register()); - } - [Test] public void TestRegisterFactoryWithoutCreate() { @@ -217,6 +211,13 @@ namespace Test.LightweightIocContainer Assert.IsInstanceOf(resolvedTest); } + [Test] + public void TestResolveInterfaceWithoutImplementation() + { + _iocContainer.Register(); + Assert.Throws(() => _iocContainer.Resolve()); + } + [Test] public void TestResolveWithParams() {