From 62627ca923a3ffa2477a3bba8961e684038a63fb Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Fri, 22 Nov 2019 10:38:28 +0100 Subject: [PATCH 1/9] - Refactoring, prepare for #29: - rename IRegistrationBase to IRegistration - split IDefaultRegistraton into IRegistrationBase and a new IDefaultRegistration (same with implementations) --- .../UnknownRegistrationException.cs | 4 +- .../Interfaces/IIocContainer.cs | 20 +- .../Interfaces/Installers/IIocInstaller.cs | 2 +- .../Registrations/IDefaultRegistration.cs | 31 +-- .../Interfaces/Registrations/IRegistration.cs | 24 +++ .../Registrations/IRegistrationBase.cs | 22 +- .../ITypedFactoryRegistration.cs | 2 +- .../IUnitTestCallbackRegistration.cs | 4 +- LightweightIocContainer/IocContainer.cs | 36 ++-- LightweightIocContainer/Lifestyle.cs | 2 +- .../LightweightIocContainer.xml | 193 ++++++++++-------- .../Registrations/DefaultRegistration.cs | 59 +----- .../Registrations/RegistrationBase.cs | 62 ++++++ .../Registrations/RegistrationFactory.cs | 14 +- .../UnitTestCallbackRegistration.cs | 4 +- .../AssemblyInstallerTest.cs | 4 +- ...trationTest.cs => RegistrationBaseTest.cs} | 4 +- 17 files changed, 273 insertions(+), 214 deletions(-) create mode 100644 LightweightIocContainer/Interfaces/Registrations/IRegistration.cs create mode 100644 LightweightIocContainer/Registrations/RegistrationBase.cs rename Test.LightweightIocContainer/{DefaultRegistrationTest.cs => RegistrationBaseTest.cs} (83%) diff --git a/LightweightIocContainer/Exceptions/UnknownRegistrationException.cs b/LightweightIocContainer/Exceptions/UnknownRegistrationException.cs index 0a83286..50b25c2 100644 --- a/LightweightIocContainer/Exceptions/UnknownRegistrationException.cs +++ b/LightweightIocContainer/Exceptions/UnknownRegistrationException.cs @@ -8,12 +8,12 @@ using LightweightIocContainer.Interfaces.Registrations; namespace LightweightIocContainer.Exceptions { /// - /// An unknown was used + /// An unknown was used /// internal class UnknownRegistrationException : Exception { /// - /// An unknown was used + /// An unknown was used /// /// The exception message public UnknownRegistrationException(string message) diff --git a/LightweightIocContainer/Interfaces/IIocContainer.cs b/LightweightIocContainer/Interfaces/IIocContainer.cs index afa8af9..9a667b8 100644 --- a/LightweightIocContainer/Interfaces/IIocContainer.cs +++ b/LightweightIocContainer/Interfaces/IIocContainer.cs @@ -10,7 +10,7 @@ using LightweightIocContainer.Interfaces.Registrations; namespace LightweightIocContainer.Interfaces { /// - /// The main container that carries all the s and can resolve all the types you'll ever want + /// The main container that carries all the s and can resolve all the types you'll ever want /// public interface IIocContainer : IDisposable { @@ -26,17 +26,17 @@ namespace LightweightIocContainer.Interfaces /// /// The Interface to register /// The Type that implements the interface - /// The for this - /// The created - IDefaultRegistration Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface; + /// The for this + /// The created + IRegistrationBase Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface; /// /// Register a without an interface /// /// The to register - /// The for this - /// The created - IDefaultRegistration Register(Lifestyle lifestyle = Lifestyle.Transient); + /// The for this + /// The created + IRegistrationBase Register(Lifestyle lifestyle = Lifestyle.Transient); /// /// Register an Interface with a Type that implements it as a multiton @@ -44,14 +44,14 @@ namespace LightweightIocContainer.Interfaces /// The Interface to register /// The Type that implements the interface /// The Type of the multiton scope - /// The created + /// The created IMultitonRegistration Register() where TImplementation : TInterface; /// /// Register an Interface as an abstract typed factory /// /// The abstract typed factory to register - /// The created + /// The created ITypedFactoryRegistration RegisterFactory(); /// @@ -59,7 +59,7 @@ namespace LightweightIocContainer.Interfaces /// /// The Interface to register /// The for the callback - /// The created + /// The created IUnitTestCallbackRegistration RegisterUnitTestCallback(ResolveCallback unitTestCallback); /// diff --git a/LightweightIocContainer/Interfaces/Installers/IIocInstaller.cs b/LightweightIocContainer/Interfaces/Installers/IIocInstaller.cs index fb99ce6..d329bd4 100644 --- a/LightweightIocContainer/Interfaces/Installers/IIocInstaller.cs +++ b/LightweightIocContainer/Interfaces/Installers/IIocInstaller.cs @@ -12,7 +12,7 @@ namespace LightweightIocContainer.Interfaces.Installers public interface IIocInstaller { /// - /// Install the needed s in the given + /// Install the needed s in the given /// /// The current void Install(IIocContainer container); diff --git a/LightweightIocContainer/Interfaces/Registrations/IDefaultRegistration.cs b/LightweightIocContainer/Interfaces/Registrations/IDefaultRegistration.cs index 6c57fd4..3597927 100644 --- a/LightweightIocContainer/Interfaces/Registrations/IDefaultRegistration.cs +++ b/LightweightIocContainer/Interfaces/Registrations/IDefaultRegistration.cs @@ -1,39 +1,20 @@ -// Author: simon.gockner -// Created: 2019-05-20 +// Author: Gockner, Simon +// Created: 2019-11-22 // Copyright(c) 2019 SimonG. All Rights Reserved. using System; -using LightweightIocContainer.Interfaces.Installers; namespace LightweightIocContainer.Interfaces.Registrations { /// - /// The default registration that is used to register a for the Interface it implements + /// The to register a for the Interface it implements /// - /// The registered Interface - public interface IDefaultRegistration : IRegistrationBase + /// The of the interface + public interface IDefaultRegistration : IRegistrationBase { /// - /// The that implements the that is registered with this + /// The that implements the that is registered with this /// Type ImplementationType { get; } - - /// - /// The Lifestyle of Instances that are created with this - /// - Lifestyle Lifestyle { get; } - - /// - /// This is invoked when an instance of this type is created. - /// Can be set in the by calling - /// - Action OnCreateAction { get; } - - /// - /// Pass an that will be invoked when an instance of this type is created - /// - /// The - /// The current instance of this - IDefaultRegistration OnCreate(Action action); } } \ No newline at end of file diff --git a/LightweightIocContainer/Interfaces/Registrations/IRegistration.cs b/LightweightIocContainer/Interfaces/Registrations/IRegistration.cs new file mode 100644 index 0000000..65d6dd6 --- /dev/null +++ b/LightweightIocContainer/Interfaces/Registrations/IRegistration.cs @@ -0,0 +1,24 @@ +// Author: simon.gockner +// Created: 2019-05-20 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +using System; + +namespace LightweightIocContainer.Interfaces.Registrations +{ + /// + /// The base registration that is used to register an Interface + /// + public interface IRegistration + { + /// + /// The name of the + /// + string Name { get; } + + /// + /// The of the Interface that is registered with this + /// + Type InterfaceType { get; } + } +} \ No newline at end of file diff --git a/LightweightIocContainer/Interfaces/Registrations/IRegistrationBase.cs b/LightweightIocContainer/Interfaces/Registrations/IRegistrationBase.cs index cc39e41..d106bbf 100644 --- a/LightweightIocContainer/Interfaces/Registrations/IRegistrationBase.cs +++ b/LightweightIocContainer/Interfaces/Registrations/IRegistrationBase.cs @@ -3,22 +3,32 @@ // Copyright(c) 2019 SimonG. All Rights Reserved. using System; +using LightweightIocContainer.Interfaces.Installers; namespace LightweightIocContainer.Interfaces.Registrations { /// - /// The base registration that is used to register an Interface + /// The that is used to register an Interface /// - public interface IRegistrationBase + /// The registered Interface + public interface IRegistrationBase : IRegistration { /// - /// The name of the + /// The Lifestyle of Instances that are created with this /// - string Name { get; } + Lifestyle Lifestyle { get; } /// - /// The of the Interface that is registered with this + /// This is invoked when an instance of this type is created. + /// Can be set in the by calling /// - Type InterfaceType { get; } + Action OnCreateAction { get; } + + /// + /// Pass an that will be invoked when an instance of this type is created + /// + /// The + /// The current instance of this + IRegistrationBase OnCreate(Action action); } } \ No newline at end of file diff --git a/LightweightIocContainer/Interfaces/Registrations/ITypedFactoryRegistration.cs b/LightweightIocContainer/Interfaces/Registrations/ITypedFactoryRegistration.cs index 584a290..12c361e 100644 --- a/LightweightIocContainer/Interfaces/Registrations/ITypedFactoryRegistration.cs +++ b/LightweightIocContainer/Interfaces/Registrations/ITypedFactoryRegistration.cs @@ -10,7 +10,7 @@ namespace LightweightIocContainer.Interfaces.Registrations /// The registration that is used to register an abstract typed factory /// /// The type of the abstract typed factory - public interface ITypedFactoryRegistration : IRegistrationBase + public interface ITypedFactoryRegistration : IRegistration { /// /// The class that contains the implemented abstract factory of this diff --git a/LightweightIocContainer/Interfaces/Registrations/IUnitTestCallbackRegistration.cs b/LightweightIocContainer/Interfaces/Registrations/IUnitTestCallbackRegistration.cs index e5831d8..2dd0123 100644 --- a/LightweightIocContainer/Interfaces/Registrations/IUnitTestCallbackRegistration.cs +++ b/LightweightIocContainer/Interfaces/Registrations/IUnitTestCallbackRegistration.cs @@ -5,10 +5,10 @@ namespace LightweightIocContainer.Interfaces.Registrations { /// - /// A special that allows to set a as a callback that is called on + /// A special that allows to set a as a callback that is called on /// /// - public interface IUnitTestCallbackRegistration : IRegistrationBase + public interface IUnitTestCallbackRegistration : IRegistration { /// /// An that is set as a callback that is called on diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index 0112cfd..4c9df25 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -17,19 +17,19 @@ using LightweightIocContainer.Registrations; namespace LightweightIocContainer { /// - /// The main container that carries all the s and can resolve all the types you'll ever want + /// The main container that carries all the s and can resolve all the types you'll ever want /// public class IocContainer : IIocContainer { private readonly RegistrationFactory _registrationFactory; - private readonly List _registrations = new List(); + private readonly List _registrations = new List(); private readonly List<(Type type, object instance)> _singletons = new List<(Type, object)>(); private readonly List<(Type type, Type scope, ConditionalWeakTable instances)> _multitons = new List<(Type, Type, ConditionalWeakTable)>(); /// - /// The main container that carries all the s and can resolve all the types you'll ever want + /// The main container that carries all the s and can resolve all the types you'll ever want /// public IocContainer() { @@ -57,11 +57,11 @@ namespace LightweightIocContainer /// /// The Interface to register /// The Type that implements the interface - /// The for this - /// The created - public IDefaultRegistration Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface + /// The for this + /// The created + public IRegistrationBase Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface { - IDefaultRegistration registration = _registrationFactory.Register(lifestyle); + IRegistrationBase registration = _registrationFactory.Register(lifestyle); Register(registration); return registration; @@ -71,11 +71,11 @@ namespace LightweightIocContainer /// Register a without an interface /// /// The to register - /// The for this - /// The created - public IDefaultRegistration Register(Lifestyle lifestyle = Lifestyle.Transient) + /// The for this + /// The created + public IRegistrationBase Register(Lifestyle lifestyle = Lifestyle.Transient) { - IDefaultRegistration registration = _registrationFactory.Register(lifestyle); + IRegistrationBase registration = _registrationFactory.Register(lifestyle); Register(registration); return registration; @@ -87,7 +87,7 @@ namespace LightweightIocContainer /// The Interface to register /// The Type that implements the interface /// The Type of the multiton scope - /// The created + /// The created public IMultitonRegistration Register() where TImplementation : TInterface { IMultitonRegistration registration = _registrationFactory.Register(); @@ -100,7 +100,7 @@ namespace LightweightIocContainer /// Register an Interface as an abstract typed factory /// /// The abstract typed factory to register - /// The created + /// The created public ITypedFactoryRegistration RegisterFactory() { ITypedFactoryRegistration registration = _registrationFactory.RegisterFactory(); @@ -114,7 +114,7 @@ namespace LightweightIocContainer /// /// The Interface to register /// The for the callback - /// The created + /// The created public IUnitTestCallbackRegistration RegisterUnitTestCallback(ResolveCallback unitTestCallback) { IUnitTestCallbackRegistration registration = _registrationFactory.RegisterUnitTestCallback(unitTestCallback); @@ -124,11 +124,11 @@ namespace LightweightIocContainer } /// - /// Add the to the the + /// Add the to the the /// - /// The given + /// The given /// The is already registered in this - private void Register(IRegistrationBase registration) + private void Register(IRegistration registration) { //if type is already registered if (_registrations.Any(r => r.InterfaceType == registration.InterfaceType)) @@ -195,7 +195,7 @@ namespace LightweightIocContainer /// The registration for the given has an unknown private T ResolveInternal(object[] arguments, List resolveStack = null) { - IRegistrationBase registration = _registrations.FirstOrDefault(r => r.InterfaceType == typeof(T)); + IRegistration registration = _registrations.FirstOrDefault(r => r.InterfaceType == typeof(T)); if (registration == null) throw new TypeNotRegisteredException(typeof(T)); diff --git a/LightweightIocContainer/Lifestyle.cs b/LightweightIocContainer/Lifestyle.cs index d3dc7fc..a7141d3 100644 --- a/LightweightIocContainer/Lifestyle.cs +++ b/LightweightIocContainer/Lifestyle.cs @@ -7,7 +7,7 @@ using LightweightIocContainer.Interfaces.Registrations; namespace LightweightIocContainer { /// - /// The Lifestyles that can be used for a + /// The Lifestyles that can be used for a /// public enum Lifestyle { diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index e0c48cb..abd02ce 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -222,12 +222,12 @@ - An unknown was used + An unknown was used - An unknown was used + An unknown was used The exception message @@ -295,7 +295,7 @@ - The main container that carries all the s and can resolve all the types you'll ever want + The main container that carries all the s and can resolve all the types you'll ever want @@ -311,16 +311,16 @@ The Interface to register The Type that implements the interface - The for this - The created + The for this + The created Register a without an interface The to register - The for this - The created + The for this + The created @@ -329,14 +329,14 @@ The Interface to register The Type that implements the interface The Type of the multiton scope - The created + The created Register an Interface as an abstract typed factory The abstract typed factory to register - The created + The created @@ -344,7 +344,7 @@ The Interface to register The for the callback - The created + The created @@ -391,64 +391,70 @@ - Install the needed s in the given + Install the needed s in the given The current - The default registration that is used to register a for the Interface it implements + The to register a for the Interface it implements - The registered Interface + The of the interface - The that implements the that is registered with this + The that implements the that is registered with this - + - The Lifestyle of Instances that are created with this + The registration that is used to register a multiton + The registered interface - + - This is invoked when an instance of this type is created. - Can be set in the by calling + The of the multiton scope - + - Pass an that will be invoked when an instance of this type is created + The base registration that is used to register an Interface - The - The current instance of this - + - The registration that is used to register a multiton + The name of the - The registered interface - + - The of the multiton scope + The of the Interface that is registered with this - + - The base registration that is used to register an Interface + The that is used to register an Interface + The registered Interface - + - The name of the + The Lifestyle of Instances that are created with this - + - The of the Interface that is registered with this + This is invoked when an instance of this type is created. + Can be set in the by calling + + + + + Pass an that will be invoked when an instance of this type is created + The + The current instance of this @@ -463,7 +469,7 @@ - A special that allows to set a as a callback that is called on + A special that allows to set a as a callback that is called on @@ -474,12 +480,12 @@ - The main container that carries all the s and can resolve all the types you'll ever want + The main container that carries all the s and can resolve all the types you'll ever want - The main container that carries all the s and can resolve all the types you'll ever want + The main container that carries all the s and can resolve all the types you'll ever want @@ -495,16 +501,16 @@ The Interface to register The Type that implements the interface - The for this - The created + The for this + The created Register a without an interface The to register - The for this - The created + The for this + The created @@ -513,14 +519,14 @@ The Interface to register The Type that implements the interface The Type of the multiton scope - The created + The created Register an Interface as an abstract typed factory The abstract typed factory to register - The created + The created @@ -528,13 +534,13 @@ The Interface to register The for the callback - The created + The created - + - Add the to the the + Add the to the the - The given + The given The is already registered in this @@ -640,7 +646,7 @@ - The Lifestyles that can be used for a + The Lifestyles that can be used for a @@ -660,73 +666,86 @@ - The default registration that is used to register a for the Interface it implements + The to register a for the Interface it implements - The registered Interface + The of the interface - The default registration that is used to register a for the Interface it implements + The to register a for the Interface it implements - The of the Interface - The of the Implementation - The of the registration + The of the interface + The of the implementation + The of the - + - The name of the + The that implements the that is registered with this - + - The of the Interface that is registered with this + The registration that is used to register a multiton + The registered interface - + - The that implements the that is registered with this + The registration that is used to register a multiton + The of the Interface + The of the Implementation + The of the Multiton Scope - + - The of Instances that are created with this + The of the multiton scope - + - This is invoked when an instance of this type is created. - Can be set in the by calling + The that is used to register an Interface + The registered Interface - + - Pass an that will be invoked when an instance of this is created + The that is used to register an Interface - The - The current instance of this + The of the Interface + The of the registration - + - The registration that is used to register a multiton + The name of the - The registered interface - + - The registration that is used to register a multiton + The of the Interface that is registered with this - The of the Interface - The of the Implementation - The of the Multiton Scope - + - The of the multiton scope + The of Instances that are created with this + + + + + This is invoked when an instance of this type is created. + Can be set in the by calling + + + + + Pass an that will be invoked when an instance of this is created + The + The current instance of this - A factory to register interfaces and factories in an and create the needed s + A factory to register interfaces and factories in an and create the needed s @@ -735,16 +754,16 @@ The Interface to register The Type that implements the interface - The for this - A new created with the given parameters + The for this + A new created with the given parameters - 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 for this + A new created with the given parameters @@ -799,13 +818,13 @@ - A special that allows to set a as a callback that is called on + A special that allows to set a as a callback that is called on - A special that allows to set a as a callback that is called on + A special that allows to set a as a callback that is called on The of the interface The that is set as a callback diff --git a/LightweightIocContainer/Registrations/DefaultRegistration.cs b/LightweightIocContainer/Registrations/DefaultRegistration.cs index 9aa0b34..f8c5c47 100644 --- a/LightweightIocContainer/Registrations/DefaultRegistration.cs +++ b/LightweightIocContainer/Registrations/DefaultRegistration.cs @@ -1,71 +1,34 @@ -// Author: simon.gockner -// Created: 2019-05-20 +// Author: Gockner, Simon +// Created: 2019-11-22 // Copyright(c) 2019 SimonG. All Rights Reserved. using System; -using LightweightIocContainer.Interfaces.Installers; using LightweightIocContainer.Interfaces.Registrations; namespace LightweightIocContainer.Registrations { /// - /// The default registration that is used to register a for the Interface it implements + /// The to register a for the Interface it implements /// - /// The registered Interface - public class DefaultRegistration : IDefaultRegistration + /// The of the interface + public class DefaultRegistration : RegistrationBase, IDefaultRegistration { /// - /// The default registration that is used to register a for the Interface it implements + /// The to register a for the Interface it implements /// - /// The of the Interface - /// The of the Implementation - /// The of the registration + /// The of the interface + /// The of the implementation + /// The of the public DefaultRegistration(Type interfaceType, Type implementationType, Lifestyle lifestyle) + : base(interfaceType, lifestyle) { - InterfaceType = interfaceType; ImplementationType = implementationType; - Lifestyle = lifestyle; - Name = $"{InterfaceType.Name}, {ImplementationType.Name}, Lifestyle: {Lifestyle.ToString()}"; } /// - /// The name of the - /// - public string Name { get; } - - /// - /// The of the Interface that is registered with this - /// - public Type InterfaceType { get; } - - /// - /// The that implements the that is registered with this + /// The that implements the that is registered with this /// public Type ImplementationType { get; } - - /// - /// The of Instances that are created with this - /// - public Lifestyle Lifestyle { get; } - - - /// - /// This is invoked when an instance of this type is created. - /// Can be set in the by calling - /// - public Action OnCreateAction { get; private set; } - - - /// - /// Pass an that will be invoked when an instance of this is created - /// - /// The - /// The current instance of this - public IDefaultRegistration OnCreate(Action action) - { - OnCreateAction = action; - return this; - } } } \ No newline at end of file diff --git a/LightweightIocContainer/Registrations/RegistrationBase.cs b/LightweightIocContainer/Registrations/RegistrationBase.cs new file mode 100644 index 0000000..3c77b39 --- /dev/null +++ b/LightweightIocContainer/Registrations/RegistrationBase.cs @@ -0,0 +1,62 @@ +// Author: simon.gockner +// Created: 2019-05-20 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +using System; +using LightweightIocContainer.Interfaces.Installers; +using LightweightIocContainer.Interfaces.Registrations; + +namespace LightweightIocContainer.Registrations +{ + /// + /// The that is used to register an Interface + /// + /// The registered Interface + public abstract class RegistrationBase : IRegistrationBase + { + /// + /// The that is used to register an Interface + /// + /// The of the Interface + /// The of the registration + protected RegistrationBase(Type interfaceType, Lifestyle lifestyle) + { + InterfaceType = interfaceType; + Lifestyle = lifestyle; + } + + /// + /// The name of the + /// + public string Name { get; protected set; } + + /// + /// The of the Interface that is registered with this + /// + public Type InterfaceType { get; } + + /// + /// The of Instances that are created with this + /// + public Lifestyle Lifestyle { get; } + + + /// + /// This is invoked when an instance of this type is created. + /// Can be set in the by calling + /// + public Action OnCreateAction { get; private set; } + + + /// + /// Pass an that will be invoked when an instance of this is created + /// + /// The + /// The current instance of this + public IRegistrationBase OnCreate(Action action) + { + OnCreateAction = action; + return this; + } + } +} \ No newline at end of file diff --git a/LightweightIocContainer/Registrations/RegistrationFactory.cs b/LightweightIocContainer/Registrations/RegistrationFactory.cs index 1482424..e424f8b 100644 --- a/LightweightIocContainer/Registrations/RegistrationFactory.cs +++ b/LightweightIocContainer/Registrations/RegistrationFactory.cs @@ -11,7 +11,7 @@ using LightweightIocContainer.Interfaces.Registrations; namespace LightweightIocContainer.Registrations { /// - /// A factory to register interfaces and factories in an and create the needed s + /// A factory to register interfaces and factories in an and create the needed s /// internal class RegistrationFactory { @@ -27,20 +27,20 @@ namespace LightweightIocContainer.Registrations /// /// The Interface to register /// The Type that implements the interface - /// The for this - /// A new created with the given parameters + /// The for this + /// A new created with the given parameters public IDefaultRegistration Register(Lifestyle lifestyle) where TImplementation : TInterface { return new DefaultRegistration(typeof(TInterface), typeof(TImplementation), lifestyle); } /// - /// 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 IDefaultRegistration Register(Lifestyle lifestyle) + /// The for this + /// A new created with the given parameters + public IRegistrationBase Register(Lifestyle lifestyle) { if (typeof(TImplementation).IsInterface) throw new InvalidRegistrationException("Can't register an interface without its implementation type."); diff --git a/LightweightIocContainer/Registrations/UnitTestCallbackRegistration.cs b/LightweightIocContainer/Registrations/UnitTestCallbackRegistration.cs index 8f6e171..529d1e5 100644 --- a/LightweightIocContainer/Registrations/UnitTestCallbackRegistration.cs +++ b/LightweightIocContainer/Registrations/UnitTestCallbackRegistration.cs @@ -9,13 +9,13 @@ using LightweightIocContainer.Interfaces.Registrations; namespace LightweightIocContainer.Registrations { /// - /// A special that allows to set a as a callback that is called on + /// A special that allows to set a as a callback that is called on /// /// public class UnitTestCallbackRegistration : IUnitTestCallbackRegistration { /// - /// A special that allows to set a as a callback that is called on + /// A special that allows to set a as a callback that is called on /// /// The of the interface /// The that is set as a callback diff --git a/Test.LightweightIocContainer/AssemblyInstallerTest.cs b/Test.LightweightIocContainer/AssemblyInstallerTest.cs index 30874e0..c605328 100644 --- a/Test.LightweightIocContainer/AssemblyInstallerTest.cs +++ b/Test.LightweightIocContainer/AssemblyInstallerTest.cs @@ -25,7 +25,7 @@ namespace Test.LightweightIocContainer { public void Install(IIocContainer container) { - container.Register>(); + container.Register>(); } } @@ -54,7 +54,7 @@ namespace Test.LightweightIocContainer AssemblyInstaller assemblyInstaller = new AssemblyInstaller(assemblyMock.Object); assemblyInstaller.Install(iocContainerMock.Object); - iocContainerMock.Verify(ioc => ioc.Register>>(It.IsAny()), Times.Once); + iocContainerMock.Verify(ioc => ioc.Register>>(It.IsAny()), Times.Once); } [Test] diff --git a/Test.LightweightIocContainer/DefaultRegistrationTest.cs b/Test.LightweightIocContainer/RegistrationBaseTest.cs similarity index 83% rename from Test.LightweightIocContainer/DefaultRegistrationTest.cs rename to Test.LightweightIocContainer/RegistrationBaseTest.cs index 70cb966..8e0cd28 100644 --- a/Test.LightweightIocContainer/DefaultRegistrationTest.cs +++ b/Test.LightweightIocContainer/RegistrationBaseTest.cs @@ -13,7 +13,7 @@ using NUnit.Framework; namespace Test.LightweightIocContainer { [TestFixture] - public class DefaultRegistrationTest + public class RegistrationBaseTest { #region TestClasses @@ -37,7 +37,7 @@ namespace Test.LightweightIocContainer public void TestOnCreate() { RegistrationFactory registrationFactory = new RegistrationFactory(new Mock().Object); - IDefaultRegistration testRegistration = registrationFactory.Register(Lifestyle.Transient).OnCreate(t => t.DoSomething()); + IRegistrationBase testRegistration = registrationFactory.Register(Lifestyle.Transient).OnCreate(t => t.DoSomething()); ITest test = new Test(); From 5f3c9e28328d8860bee971f69110a93a285d1e1d Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Fri, 22 Nov 2019 11:11:49 +0100 Subject: [PATCH 2/9] #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() { From 503b58336884bcc8a01022221d9261d60529c5f6 Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Fri, 22 Nov 2019 11:15:00 +0100 Subject: [PATCH 3/9] #29: fix comments for new IDefaultRegistration --- LightweightIocContainer/Interfaces/IIocContainer.cs | 2 +- LightweightIocContainer/IocContainer.cs | 4 ++-- LightweightIocContainer/LightweightIocContainer.xml | 4 ++-- LightweightIocContainer/Registrations/RegistrationFactory.cs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/LightweightIocContainer/Interfaces/IIocContainer.cs b/LightweightIocContainer/Interfaces/IIocContainer.cs index a3162e3..f239ef1 100644 --- a/LightweightIocContainer/Interfaces/IIocContainer.cs +++ b/LightweightIocContainer/Interfaces/IIocContainer.cs @@ -28,7 +28,7 @@ namespace LightweightIocContainer.Interfaces /// The Type that implements the interface /// The for this /// The created - IRegistrationBase Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface; + IDefaultRegistration Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface; /// /// Register a without an interface diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index edd6d88..14f01af 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -59,9 +59,9 @@ namespace LightweightIocContainer /// The Type that implements the interface /// The for this /// The created - public IRegistrationBase Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface + public IDefaultRegistration Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface { - IRegistrationBase registration = _registrationFactory.Register(lifestyle); + IDefaultRegistration registration = _registrationFactory.Register(lifestyle); Register(registration); return registration; diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index 613bbb7..b82fb77 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -772,8 +772,8 @@ The Interface to register The Type that implements the interface - The for this - A new created with the given parameters + The for this + A new created with the given parameters diff --git a/LightweightIocContainer/Registrations/RegistrationFactory.cs b/LightweightIocContainer/Registrations/RegistrationFactory.cs index 8249da8..5292dcb 100644 --- a/LightweightIocContainer/Registrations/RegistrationFactory.cs +++ b/LightweightIocContainer/Registrations/RegistrationFactory.cs @@ -27,8 +27,8 @@ namespace LightweightIocContainer.Registrations /// /// The Interface to register /// The Type that implements the interface - /// The for this - /// A new created with the given parameters + /// The for this + /// A new created with the given parameters public IDefaultRegistration Register(Lifestyle lifestyle) where TImplementation : TInterface { return new DefaultRegistration(typeof(TInterface), typeof(TImplementation), lifestyle); From 6e3a1c875e5e53a1093b97e902cb59dce9653ffe Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Fri, 22 Nov 2019 11:17:11 +0100 Subject: [PATCH 4/9] #29: remove unused usings --- LightweightIocContainer/Interfaces/IIocContainer.cs | 1 - LightweightIocContainer/Registrations/RegistrationFactory.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/LightweightIocContainer/Interfaces/IIocContainer.cs b/LightweightIocContainer/Interfaces/IIocContainer.cs index f239ef1..79e4746 100644 --- a/LightweightIocContainer/Interfaces/IIocContainer.cs +++ b/LightweightIocContainer/Interfaces/IIocContainer.cs @@ -3,7 +3,6 @@ // Copyright(c) 2019 SimonG. All Rights Reserved. using System; -using LightweightIocContainer.Exceptions; using LightweightIocContainer.Interfaces.Installers; using LightweightIocContainer.Interfaces.Registrations; diff --git a/LightweightIocContainer/Registrations/RegistrationFactory.cs b/LightweightIocContainer/Registrations/RegistrationFactory.cs index 5292dcb..41ec968 100644 --- a/LightweightIocContainer/Registrations/RegistrationFactory.cs +++ b/LightweightIocContainer/Registrations/RegistrationFactory.cs @@ -3,7 +3,6 @@ // Copyright(c) 2019 SimonG. All Rights Reserved. using System; -using LightweightIocContainer.Exceptions; using LightweightIocContainer.Interfaces; using LightweightIocContainer.Interfaces.Installers; using LightweightIocContainer.Interfaces.Registrations; From f1e4f5e2f997b34ed2b1730d2f398c713550fa05 Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Fri, 22 Nov 2019 11:27:11 +0100 Subject: [PATCH 5/9] #29: mark IUnitTestResolveCallback as deprecated --- LightweightIocContainer/Interfaces/IIocContainer.cs | 1 + .../Interfaces/Registrations/IUnitTestCallbackRegistration.cs | 4 ++++ LightweightIocContainer/IocContainer.cs | 4 ++++ LightweightIocContainer/Registrations/RegistrationFactory.cs | 1 + .../Registrations/UnitTestCallbackRegistration.cs | 1 + Test.LightweightIocContainer/IocContainerTest.cs | 3 +++ .../UnitTestCallbackRegistrationTest.cs | 2 ++ 7 files changed, 16 insertions(+) diff --git a/LightweightIocContainer/Interfaces/IIocContainer.cs b/LightweightIocContainer/Interfaces/IIocContainer.cs index 79e4746..0b5d2bd 100644 --- a/LightweightIocContainer/Interfaces/IIocContainer.cs +++ b/LightweightIocContainer/Interfaces/IIocContainer.cs @@ -59,6 +59,7 @@ namespace LightweightIocContainer.Interfaces /// The Interface to register /// The for the callback /// The created + [Obsolete("RegisterUnitTestCallback is deprecated, use `WithFactoryMethod()` from ISingleTypeRegistration instead.")] IUnitTestCallbackRegistration RegisterUnitTestCallback(ResolveCallback unitTestCallback); /// diff --git a/LightweightIocContainer/Interfaces/Registrations/IUnitTestCallbackRegistration.cs b/LightweightIocContainer/Interfaces/Registrations/IUnitTestCallbackRegistration.cs index 2dd0123..d10f74e 100644 --- a/LightweightIocContainer/Interfaces/Registrations/IUnitTestCallbackRegistration.cs +++ b/LightweightIocContainer/Interfaces/Registrations/IUnitTestCallbackRegistration.cs @@ -2,17 +2,21 @@ // Created: 2019-10-15 // Copyright(c) 2019 SimonG. All Rights Reserved. +using System; + namespace LightweightIocContainer.Interfaces.Registrations { /// /// A special that allows to set a as a callback that is called on /// /// + [Obsolete("IUnitTestCallbackRegistration is deprecated, use `WithFactoryMethod()` from ISingleTypeRegistration instead.")] public interface IUnitTestCallbackRegistration : IRegistration { /// /// An that is set as a callback that is called on /// + [Obsolete("UnitTestResolveCallback is deprecated, use `WithFactoryMethod()` from ISingleTypeRegistration instead.")] ResolveCallback UnitTestResolveCallback { get; } } } \ No newline at end of file diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index 14f01af..8096bf9 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -115,6 +115,7 @@ namespace LightweightIocContainer /// The Interface to register /// The for the callback /// The created + [Obsolete("RegisterUnitTestCallback is deprecated, use `WithFactoryMethod()` from ISingleTypeRegistration instead.")] public IUnitTestCallbackRegistration RegisterUnitTestCallback(ResolveCallback unitTestCallback) { IUnitTestCallbackRegistration registration = _registrationFactory.RegisterUnitTestCallback(unitTestCallback); @@ -209,10 +210,13 @@ namespace LightweightIocContainer T resolvedInstance; + //TODO: remove this #pragma when IUnitTestCallbackRegistration is removed +#pragma warning disable 618 if (registration is IUnitTestCallbackRegistration unitTestCallbackRegistration) { resolvedInstance = unitTestCallbackRegistration.UnitTestResolveCallback.Invoke(arguments); } +#pragma warning restore 618 else if (registration is IRegistrationBase defaultRegistration) { if (defaultRegistration.Lifestyle == Lifestyle.Singleton) diff --git a/LightweightIocContainer/Registrations/RegistrationFactory.cs b/LightweightIocContainer/Registrations/RegistrationFactory.cs index 41ec968..d780413 100644 --- a/LightweightIocContainer/Registrations/RegistrationFactory.cs +++ b/LightweightIocContainer/Registrations/RegistrationFactory.cs @@ -66,6 +66,7 @@ namespace LightweightIocContainer.Registrations return new TypedFactoryRegistration(typeof(TFactory), _iocContainer); } + [Obsolete("RegisterUnitTestCallback is deprecated, use `WithFactoryMethod()` from ISingleTypeRegistration instead.")] public IUnitTestCallbackRegistration RegisterUnitTestCallback(ResolveCallback unitTestResolveCallback) { return new UnitTestCallbackRegistration(typeof(TInterface), unitTestResolveCallback); diff --git a/LightweightIocContainer/Registrations/UnitTestCallbackRegistration.cs b/LightweightIocContainer/Registrations/UnitTestCallbackRegistration.cs index 529d1e5..c489510 100644 --- a/LightweightIocContainer/Registrations/UnitTestCallbackRegistration.cs +++ b/LightweightIocContainer/Registrations/UnitTestCallbackRegistration.cs @@ -12,6 +12,7 @@ namespace LightweightIocContainer.Registrations /// A special that allows to set a as a callback that is called on /// /// + [Obsolete("UnitTestCallbackRegistration is deprecated, use `WithFactoryMethod()` from ISingleTypeRegistration instead.")] public class UnitTestCallbackRegistration : IUnitTestCallbackRegistration { /// diff --git a/Test.LightweightIocContainer/IocContainerTest.cs b/Test.LightweightIocContainer/IocContainerTest.cs index ccffa6c..39b1add 100644 --- a/Test.LightweightIocContainer/IocContainerTest.cs +++ b/Test.LightweightIocContainer/IocContainerTest.cs @@ -1,3 +1,4 @@ +using System; using JetBrains.Annotations; using LightweightIocContainer; using LightweightIocContainer.Exceptions; @@ -179,6 +180,7 @@ namespace Test.LightweightIocContainer } [Test] + [Obsolete("RegisterUnitTestCallback is deprecated, use `WithFactoryMethod()` from ISingleTypeRegistration instead.")] public void TestRegisterUnitTestCallback() { Assert.DoesNotThrow(() => _iocContainer.RegisterUnitTestCallback(delegate {return new Test(); })); @@ -436,6 +438,7 @@ namespace Test.LightweightIocContainer } [Test] + [Obsolete("RegisterUnitTestCallback is deprecated, use `WithFactoryMethod()` from ISingleTypeRegistration instead.")] public void TestResolveUnitTestCallbackRegistration() { ITest callbackTest = new Test(); diff --git a/Test.LightweightIocContainer/UnitTestCallbackRegistrationTest.cs b/Test.LightweightIocContainer/UnitTestCallbackRegistrationTest.cs index 0e4a099..f9392db 100644 --- a/Test.LightweightIocContainer/UnitTestCallbackRegistrationTest.cs +++ b/Test.LightweightIocContainer/UnitTestCallbackRegistrationTest.cs @@ -2,6 +2,7 @@ // Created: 2019-10-15 // Copyright(c) 2019 SimonG. All Rights Reserved. +using System; using LightweightIocContainer; using LightweightIocContainer.Interfaces; using LightweightIocContainer.Interfaces.Registrations; @@ -12,6 +13,7 @@ using NUnit.Framework; namespace Test.LightweightIocContainer { [TestFixture] + [Obsolete("UnitTestCallbackRegistration is deprecated, use `WithFactoryMethod()` from ISingleTypeRegistration instead.")] public class UnitTestCallbackRegistrationTest { private interface ITest From 62b36cb5069a7aa9000426303117a60d068fa0f7 Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Fri, 22 Nov 2019 11:42:41 +0100 Subject: [PATCH 6/9] #29: add `WithParameters()` method to IRegistrationBase: - pass parameters that will be used to create an instance of the registered type - add new method `UpdateArgumentsWithRegistrationParameters()` that handles these parameters in the `CreateInstance<>()` method --- .../Registrations/IRegistrationBase.cs | 26 +++++++ .../InternalResolvePlaceholder.cs | 14 ++++ LightweightIocContainer/IocContainer.cs | 55 ++++++++++++--- .../LightweightIocContainer.xml | 67 +++++++++++++++++-- .../Registrations/RegistrationBase.cs | 57 ++++++++++++++++ 5 files changed, 206 insertions(+), 13 deletions(-) create mode 100644 LightweightIocContainer/InternalResolvePlaceholder.cs diff --git a/LightweightIocContainer/Interfaces/Registrations/IRegistrationBase.cs b/LightweightIocContainer/Interfaces/Registrations/IRegistrationBase.cs index d106bbf..7f7e3dd 100644 --- a/LightweightIocContainer/Interfaces/Registrations/IRegistrationBase.cs +++ b/LightweightIocContainer/Interfaces/Registrations/IRegistrationBase.cs @@ -3,6 +3,7 @@ // Copyright(c) 2019 SimonG. All Rights Reserved. using System; +using LightweightIocContainer.Exceptions; using LightweightIocContainer.Interfaces.Installers; namespace LightweightIocContainer.Interfaces.Registrations @@ -30,5 +31,30 @@ namespace LightweightIocContainer.Interfaces.Registrations /// The /// The current instance of this IRegistrationBase OnCreate(Action action); + + /// + /// An of parameters that are used to an instance of this + /// Can be set in the by calling + /// + object[] Parameters { get; } + + /// + /// Pass parameters that will be used to an instance of this + /// Parameters set with this method are always inserted at the beginning of the argument list if more parameters are given when resolving + /// + /// The parameters + /// The current instance of this + /// are already set or no parameters given + IRegistrationBase WithParameters(params object[] parameters); + + /// + /// Pass parameters that will be used to an instance of this + /// Parameters set with this method are inserted at the position in the argument list that is passed with the parameter if more parameters are given when resolving + /// + /// The parameters with their position + /// The current instance of this + /// are already set or no parameters given + IRegistrationBase WithParameters(params (int index, object parameter)[] parameters); + } } \ No newline at end of file diff --git a/LightweightIocContainer/InternalResolvePlaceholder.cs b/LightweightIocContainer/InternalResolvePlaceholder.cs new file mode 100644 index 0000000..8f97059 --- /dev/null +++ b/LightweightIocContainer/InternalResolvePlaceholder.cs @@ -0,0 +1,14 @@ +// Author: Gockner, Simon +// Created: 2019-11-22 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +namespace LightweightIocContainer +{ + /// + /// An internal placeholder that is used during the resolving process + /// + internal class InternalResolvePlaceholder + { + + } +} \ No newline at end of file diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index 8096bf9..dd6b580 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -312,6 +312,9 @@ namespace LightweightIocContainer /// A newly created instance of the given private T CreateInstance(IRegistrationBase registration, object[] arguments, List resolveStack) { + if (registration.Parameters != null) + arguments = UpdateArgumentsWithRegistrationParameters(registration, arguments); + T instance; if (registration is IDefaultRegistration defaultRegistration) { @@ -339,6 +342,50 @@ namespace LightweightIocContainer return instance; } + /// + /// Update the given arguments with the of the given + /// + /// The given + /// The of the given + /// The constructor arguments + /// The argument list updated with the + private object[] UpdateArgumentsWithRegistrationParameters(IRegistrationBase registration, object[] arguments) + { + if (arguments != null && arguments.Any()) //if more arguments were passed to resolve + { + int argumentsSize = registration.Parameters.Length + arguments.Length; + object[] newArguments = new object[argumentsSize]; + + for (int i = 0; i < argumentsSize; i++) + { + if (i < registration.Parameters.Length) //if `i` is bigger than the length of the parameters, take the given arguments + { + object currentParameter = registration.Parameters[i]; + if (!(currentParameter is InternalResolvePlaceholder)) //use the parameter at the current index if it is not a placeholder + { + newArguments[i] = currentParameter; + continue; + } + } + + object firstArgument = arguments.FirstOrGiven(a => !(a is InternalResolvePlaceholder)); //find the first argument that is not a placeholder + if (firstArgument is InternalResolvePlaceholder) //no more arguments available + break; //there won't be any more arguments + + newArguments[i] = firstArgument; + + int indexOfFirstArgument = Array.IndexOf(arguments, firstArgument); + arguments[indexOfFirstArgument] = new InternalResolvePlaceholder(); + } + + arguments = newArguments; + } + else //no more arguments were passed to resolve -> only use parameters set during registration + arguments = registration.Parameters; + + return arguments; + } + /// /// Resolve the missing constructor arguments /// @@ -456,13 +503,5 @@ namespace LightweightIocContainer _singletons.Clear(); _multitons.Clear(); } - - /// - /// An internal placeholder that is used during the resolving process - /// - private class InternalResolvePlaceholder - { - - } } } \ No newline at end of file diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index b82fb77..067c4f7 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -456,6 +456,30 @@ The The current instance of this + + + An of parameters that are used to an instance of this + Can be set in the by calling + + + + + Pass parameters that will be used to an instance of this + Parameters set with this method are always inserted at the beginning of the argument list if more parameters are given when resolving + + The parameters + The current instance of this + are already set or no parameters given + + + + Pass parameters that will be used to an instance of this + Parameters set with this method are inserted at the position in the argument list that is passed with the parameter if more parameters are given when resolving + + The parameters with their position + The current instance of this + are already set or no parameters given + The to register either only an interface or only a @@ -496,6 +520,11 @@ An that is set as a callback that is called on + + + An internal placeholder that is used during the resolving process + + The main container that carries all the s and can resolve all the types you'll ever want @@ -629,6 +658,15 @@ The current resolve stack A newly created instance of the given + + + Update the given arguments with the of the given + + The given + The of the given + The constructor arguments + The argument list updated with the + Resolve the missing constructor arguments @@ -657,11 +695,6 @@ The method - - - An internal placeholder that is used during the resolving process - - The Lifestyles that can be used for a @@ -761,6 +794,30 @@ The The current instance of this + + + An of parameters that are used to an instance of this + Can be set in the by calling + + + + + Pass parameters that will be used to an instance of this + Parameters set with this method are always inserted at the beginning of the argument list if more parameters are given when resolving + + The parameters + The current instance of this + are already set or no parameters given + + + + Pass parameters that will be used to an instance of this + Parameters set with this method are inserted at the position in the argument list that is passed with the parameter if more parameters are given when resolving + + The parameters with their position + The current instance of this + are already set or no parameters given + A factory to register interfaces and factories in an and create the needed s diff --git a/LightweightIocContainer/Registrations/RegistrationBase.cs b/LightweightIocContainer/Registrations/RegistrationBase.cs index 3c77b39..512fed9 100644 --- a/LightweightIocContainer/Registrations/RegistrationBase.cs +++ b/LightweightIocContainer/Registrations/RegistrationBase.cs @@ -3,6 +3,9 @@ // Copyright(c) 2019 SimonG. All Rights Reserved. using System; +using System.Linq; +using LightweightIocContainer.Exceptions; +using LightweightIocContainer.Interfaces; using LightweightIocContainer.Interfaces.Installers; using LightweightIocContainer.Interfaces.Registrations; @@ -58,5 +61,59 @@ namespace LightweightIocContainer.Registrations OnCreateAction = action; return this; } + + /// + /// An of parameters that are used to an instance of this + /// Can be set in the by calling + /// + public object[] Parameters { get; private set; } + + /// + /// Pass parameters that will be used to an instance of this + /// Parameters set with this method are always inserted at the beginning of the argument list if more parameters are given when resolving + /// + /// The parameters + /// The current instance of this + /// are already set or no parameters given + public IRegistrationBase WithParameters(params object[] parameters) + { + if (Parameters != null) + throw new InvalidRegistrationException($"Don't use `WithParameters()` method twice (Type: {InterfaceType})."); + + if (parameters == null || !parameters.Any()) + throw new InvalidRegistrationException($"No parameters given to `WithParameters()` method (Type: {InterfaceType})."); + + Parameters = parameters; + return this; + } + + /// + /// Pass parameters that will be used to an instance of this + /// Parameters set with this method are inserted at the position in the argument list that is passed with the parameter if more parameters are given when resolving + /// + /// The parameters with their position + /// The current instance of this + /// are already set or no parameters given + public IRegistrationBase WithParameters(params (int index, object parameter)[] parameters) + { + if (Parameters != null) + throw new InvalidRegistrationException($"Don't use `WithParameters()` method twice (Type: {InterfaceType})."); + + if (parameters == null || !parameters.Any()) + throw new InvalidRegistrationException($"No parameters given to `WithParameters()` method (Type: {InterfaceType})."); + + var lastIndex = parameters.Max(p => p.index); + Parameters = new object[lastIndex + 1]; + + for (int i = 0; i < Parameters.Length; i++) + { + if (parameters.Any(p => p.index == i)) + Parameters[i] = parameters.First(p => p.index == i).parameter; + else + Parameters[i] = new InternalResolvePlaceholder(); + } + + return this; + } } } \ No newline at end of file From 3c636a786eeac278f413e381a53c4315bb491b4f Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Fri, 22 Nov 2019 14:27:29 +0100 Subject: [PATCH 7/9] #29: add unit test for withParameters method --- .../IocContainerParameterRegistrationTest.cs | 169 ++++++++++++++++++ .../RegistrationBaseTest.cs | 72 ++++++++ 2 files changed, 241 insertions(+) create mode 100644 Test.LightweightIocContainer/IocContainerParameterRegistrationTest.cs diff --git a/Test.LightweightIocContainer/IocContainerParameterRegistrationTest.cs b/Test.LightweightIocContainer/IocContainerParameterRegistrationTest.cs new file mode 100644 index 0000000..be23d2e --- /dev/null +++ b/Test.LightweightIocContainer/IocContainerParameterRegistrationTest.cs @@ -0,0 +1,169 @@ +// Author: Gockner, Simon +// Created: 2019-11-22 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +using JetBrains.Annotations; +using LightweightIocContainer; +using LightweightIocContainer.Interfaces; +using NUnit.Framework; + +namespace Test.LightweightIocContainer +{ + [TestFixture] + // ReSharper disable MemberHidesStaticFromOuterClass + public class IocContainerParameterRegistrationTest + { + #region TestClasses + + [UsedImplicitly] + public interface IA + { + IB B { get; } + IC C { get; } + } + + [UsedImplicitly] + public interface IB + { + + } + + [UsedImplicitly] + public interface IC + { + + } + + [UsedImplicitly] + public interface ID + { + IA A { get; } + IA A2 { get; } + IB B { get; } + IC C { get; } + } + + [UsedImplicitly] + private class A : IA + { + public A(IB b, IC c) + { + B = b; + C = c; + } + + public IB B { get; } + public IC C { get; } + } + + [UsedImplicitly] + private class B : IB + { + public B(IC c) + { + } + } + + [UsedImplicitly] + private class C : IC + { + + } + + [UsedImplicitly] + private class D : ID + { + public D(IA a, IA a2, IB b, IC c) + { + A = a; + A2 = a2; + B = b; + C = c; + } + + public IA A { get; } + public IA A2 { get; } + public IB B { get; } + public IC C { get; } + } + + #endregion TestClasses + + private IIocContainer _iocContainer; + + [SetUp] + public void SetUp() + { + _iocContainer = new IocContainer(); + } + + + [TearDown] + public void TearDown() + { + _iocContainer.Dispose(); + } + + + [Test] + public void TestResolveOnlyRegistrationParameters() + { + IC c = new C(); + IB b = new B(c); + + _iocContainer.Register().WithParameters(b, c); + IA a = _iocContainer.Resolve(); + + Assert.AreEqual(b, a.B); + Assert.AreEqual(c, a.C); + } + + [Test] + public void TestResolveRegistrationAndResolveParameters() + { + IC c = new C(); + IB b = new B(c); + + _iocContainer.Register().WithParameters(b); + IA a = _iocContainer.Resolve(c); + + Assert.AreEqual(b, a.B); + Assert.AreEqual(c, a.C); + } + + [Test] + public void TestResolveRegistrationAndResolveParametersMixedOrder() + { + IC c = new C(); + IB b = new B(c); + IA a = new A(b, c); + IA a2 = new A(b, c); + + _iocContainer.Register().WithParameters((0, a), (2, b), (3, c)); + ID d = _iocContainer.Resolve(a2); + + Assert.AreEqual(a, d.A); + Assert.AreEqual(a2, d.A2); + Assert.AreEqual(b, d.B); + Assert.AreEqual(c, d.C); + } + + [Test] + public void TestResolveRegistrationParametersAndResolvedParameters() + { + IC c = new C(); + IB b = new B(c); + IA a = new A(b, c); + IA a2 = new A(b, c); + + _iocContainer.Register().WithParameters(a2); + _iocContainer.Register(); + _iocContainer.Register(); + + ID d = _iocContainer.Resolve(a); + + Assert.AreEqual(a, d.A2); + Assert.AreEqual(a2, d.A); + } + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer/RegistrationBaseTest.cs b/Test.LightweightIocContainer/RegistrationBaseTest.cs index 8e0cd28..b29afa7 100644 --- a/Test.LightweightIocContainer/RegistrationBaseTest.cs +++ b/Test.LightweightIocContainer/RegistrationBaseTest.cs @@ -3,7 +3,9 @@ // Copyright(c) 2019 SimonG. All Rights Reserved. using System; +using JetBrains.Annotations; using LightweightIocContainer; +using LightweightIocContainer.Exceptions; using LightweightIocContainer.Interfaces; using LightweightIocContainer.Interfaces.Registrations; using LightweightIocContainer.Registrations; @@ -22,6 +24,16 @@ namespace Test.LightweightIocContainer void DoSomething(); } + private interface IFoo + { + + } + + private interface IBar + { + + } + private class Test : ITest { public void DoSomething() @@ -30,6 +42,20 @@ namespace Test.LightweightIocContainer } } + [UsedImplicitly] + private class Foo : IFoo + { + public Foo(IBar bar, ITest test) + { + + } + } + + private class Bar : IBar + { + + } + #endregion @@ -43,5 +69,51 @@ namespace Test.LightweightIocContainer Assert.Throws(() => testRegistration.OnCreateAction(test)); } + + [Test] + public void TestWithParameters() + { + RegistrationFactory registrationFactory = new RegistrationFactory(new Mock().Object); + + IBar bar = new Bar(); + ITest test = new Test(); + + IRegistrationBase testRegistration = registrationFactory.Register(Lifestyle.Transient).WithParameters(bar, test); + + Assert.AreEqual(bar, testRegistration.Parameters[0]); + Assert.AreEqual(test, testRegistration.Parameters[1]); + } + + [Test] + public void TestWithParametersDifferentOrder() + { + RegistrationFactory registrationFactory = new RegistrationFactory(new Mock().Object); + + IBar bar = new Bar(); + ITest test = new Test(); + + IRegistrationBase testRegistration = registrationFactory.Register(Lifestyle.Transient).WithParameters((0, bar), (3, test), (2, "SomeString")); + + Assert.AreEqual(bar, testRegistration.Parameters[0]); + Assert.IsInstanceOf(testRegistration.Parameters[1]); + Assert.AreEqual("SomeString", testRegistration.Parameters[2]); + Assert.AreEqual(test, testRegistration.Parameters[3]); + } + + [Test] + public void TestWithParametersCalledTwice() + { + RegistrationFactory registrationFactory = new RegistrationFactory(new Mock().Object); + Assert.Throws(() => registrationFactory.Register(Lifestyle.Transient).WithParameters(new Bar()).WithParameters(new Test())); + Assert.Throws(() => registrationFactory.Register(Lifestyle.Transient).WithParameters((0, new Bar())).WithParameters((1, new Test()))); + } + + [Test] + public void TestWithParametersNoParametersGiven() + { + RegistrationFactory registrationFactory = new RegistrationFactory(new Mock().Object); + Assert.Throws(() => registrationFactory.Register(Lifestyle.Transient).WithParameters((object[])null)); + Assert.Throws(() => registrationFactory.Register(Lifestyle.Transient).WithParameters(((int index, object parameter)[])null)); + } } } \ No newline at end of file From 47931b3dd13c7dc63d4e545ca8f2c8145e499867 Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Fri, 22 Nov 2019 14:46:13 +0100 Subject: [PATCH 8/9] #29: fix return value of `WithFactoryMethod()` --- .../Interfaces/Registrations/ISingleTypeRegistration.cs | 2 +- LightweightIocContainer/Registrations/SingleTypeRegistration.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LightweightIocContainer/Interfaces/Registrations/ISingleTypeRegistration.cs b/LightweightIocContainer/Interfaces/Registrations/ISingleTypeRegistration.cs index 8c68244..ad9f188 100644 --- a/LightweightIocContainer/Interfaces/Registrations/ISingleTypeRegistration.cs +++ b/LightweightIocContainer/Interfaces/Registrations/ISingleTypeRegistration.cs @@ -22,6 +22,6 @@ namespace LightweightIocContainer.Interfaces.Registrations /// /// The /// The current instance of this - IRegistrationBase WithFactoryMethod(Func factoryMethod); + ISingleTypeRegistration WithFactoryMethod(Func factoryMethod); } } \ No newline at end of file diff --git a/LightweightIocContainer/Registrations/SingleTypeRegistration.cs b/LightweightIocContainer/Registrations/SingleTypeRegistration.cs index 5a7a793..7a961c3 100644 --- a/LightweightIocContainer/Registrations/SingleTypeRegistration.cs +++ b/LightweightIocContainer/Registrations/SingleTypeRegistration.cs @@ -35,7 +35,7 @@ namespace LightweightIocContainer.Registrations /// /// The /// The current instance of this - public IRegistrationBase WithFactoryMethod(Func factoryMethod) + public ISingleTypeRegistration WithFactoryMethod(Func factoryMethod) { FactoryMethod = factoryMethod; return this; From cc52cd33617c1a9f24c1735030d1307b677b3aba Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Fri, 22 Nov 2019 15:07:32 +0100 Subject: [PATCH 9/9] #29: add ISingleTypeRegistration unit test --- .../IocContainerTest.cs | 27 ++++++++ .../SingleTypeRegistrationTest.cs | 61 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 Test.LightweightIocContainer/SingleTypeRegistrationTest.cs diff --git a/Test.LightweightIocContainer/IocContainerTest.cs b/Test.LightweightIocContainer/IocContainerTest.cs index 39b1add..717255b 100644 --- a/Test.LightweightIocContainer/IocContainerTest.cs +++ b/Test.LightweightIocContainer/IocContainerTest.cs @@ -44,6 +44,11 @@ namespace Test.LightweightIocContainer void ClearMultitonInstance(); } + private interface IFoo + { + + } + private class Test : ITest { @@ -61,6 +66,11 @@ namespace Test.LightweightIocContainer { } + + public TestConstructor(IFoo foo, string name) + { + + } } [UsedImplicitly] @@ -84,6 +94,12 @@ namespace Test.LightweightIocContainer } } + [UsedImplicitly] + private class Foo : IFoo + { + + } + public class MultitonScope { @@ -449,6 +465,17 @@ namespace Test.LightweightIocContainer Assert.AreEqual(callbackTest, test); } + [Test] + public void TestResolveSingleTypeRegistrationWithFactoryMethod() + { + _iocContainer.Register(); + _iocContainer.Register().WithFactoryMethod(c => new TestConstructor(c.Resolve(), "someName")); + + ITest test = _iocContainer.Resolve(); + + Assert.NotNull(test); + } + [Test] public void TestIsTypeRegistered() { diff --git a/Test.LightweightIocContainer/SingleTypeRegistrationTest.cs b/Test.LightweightIocContainer/SingleTypeRegistrationTest.cs new file mode 100644 index 0000000..b8bf268 --- /dev/null +++ b/Test.LightweightIocContainer/SingleTypeRegistrationTest.cs @@ -0,0 +1,61 @@ +// Author: Gockner, Simon +// Created: 2019-11-22 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +using JetBrains.Annotations; +using LightweightIocContainer; +using LightweightIocContainer.Interfaces; +using LightweightIocContainer.Interfaces.Registrations; +using LightweightIocContainer.Registrations; +using Moq; +using NUnit.Framework; + +namespace Test.LightweightIocContainer +{ + [TestFixture] + // ReSharper disable MemberHidesStaticFromOuterClass + public class SingleTypeRegistrationTest + { + private interface IFoo + { + IBar Bar { get; } + } + + private interface IBar + { + + } + + [UsedImplicitly] + private class Foo : IFoo + { + public Foo(IBar bar) + { + Bar = bar; + } + + public IBar Bar { get; } + } + + [UsedImplicitly] + private class Bar : IBar + { + + } + + [Test] + public void TestSingleTypeRegistrationWithFactoryMethod() + { + IBar bar = new Bar(); + + Mock iocContainerMock = new Mock(); + iocContainerMock.Setup(c => c.Resolve()).Returns(bar); + + RegistrationFactory registrationFactory = new RegistrationFactory(iocContainerMock.Object); + ISingleTypeRegistration registration = registrationFactory.Register(Lifestyle.Transient).WithFactoryMethod(c => new Foo(c.Resolve())); + + IFoo foo = registration.FactoryMethod(iocContainerMock.Object); + Assert.AreEqual(bar, foo.Bar); + } + } +} \ No newline at end of file