#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
pull/32/head
Simon Gockner 6 years ago
parent 62627ca923
commit 5f3c9e2832
  1. 2
      LightweightIocContainer/Interfaces/IIocContainer.cs
  2. 27
      LightweightIocContainer/Interfaces/Registrations/ISingleTypeRegistration.cs
  3. 34
      LightweightIocContainer/IocContainer.cs
  4. 55
      LightweightIocContainer/LightweightIocContainer.xml
  5. 15
      LightweightIocContainer/Registrations/RegistrationFactory.cs
  6. 44
      LightweightIocContainer/Registrations/SingleTypeRegistration.cs
  7. 13
      Test.LightweightIocContainer/IocContainerTest.cs

@ -36,7 +36,7 @@ namespace LightweightIocContainer.Interfaces
/// <typeparam name="TImplementation">The <see cref="Type"/> to register</typeparam>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="IRegistrationBase{TInterface}"/></param>
/// <returns>The created <see cref="IRegistration"/></returns>
IRegistrationBase<TImplementation> Register<TImplementation>(Lifestyle lifestyle = Lifestyle.Transient);
ISingleTypeRegistration<TImplementation> Register<TImplementation>(Lifestyle lifestyle = Lifestyle.Transient);
/// <summary>
/// Register an Interface with a Type that implements it as a multiton

@ -0,0 +1,27 @@
// Author: Gockner, Simon
// Created: 2019-11-22
// Copyright(c) 2019 SimonG. All Rights Reserved.
using System;
namespace LightweightIocContainer.Interfaces.Registrations
{
/// <summary>
/// The <see cref="IRegistrationBase{TInterface}"/> to register either only an interface or only a <see cref="Type"/>
/// </summary>
/// <typeparam name="T">The <see cref="Type"/> of the <see cref="IRegistrationBase{TInterface}"/></typeparam>
public interface ISingleTypeRegistration<T> : IRegistrationBase<T>
{
/// <summary>
/// <see cref="Func{T,TResult}"/> that is invoked instead of creating an instance of this <see cref="Type"/> the default way
/// </summary>
Func<IIocContainer, T> FactoryMethod { get; }
/// <summary>
/// Pass a <see cref="Func{T,TResult}"/> that will be invoked instead of creating an instance of this <see cref="Type"/> the default way
/// </summary>
/// <param name="factoryMethod">The <see cref="Func{T,TResult}"/></param>
/// <returns>The current instance of this <see cref="IRegistrationBase{TInterface}"/></returns>
IRegistrationBase<T> WithFactoryMethod(Func<IIocContainer, T> factoryMethod);
}
}

@ -73,9 +73,9 @@ namespace LightweightIocContainer
/// <typeparam name="TImplementation">The <see cref="Type"/> to register</typeparam>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="IRegistrationBase{TInterface}"/></param>
/// <returns>The created <see cref="IRegistration"/></returns>
public IRegistrationBase<TImplementation> Register<TImplementation>(Lifestyle lifestyle = Lifestyle.Transient)
public ISingleTypeRegistration<TImplementation> Register<TImplementation>(Lifestyle lifestyle = Lifestyle.Transient)
{
IRegistrationBase<TImplementation> registration = _registrationFactory.Register<TImplementation>(lifestyle);
ISingleTypeRegistration<TImplementation> registration = _registrationFactory.Register<TImplementation>(lifestyle);
Register(registration);
return registration;
@ -213,7 +213,7 @@ namespace LightweightIocContainer
{
resolvedInstance = unitTestCallbackRegistration.UnitTestResolveCallback.Invoke(arguments);
}
else if (registration is IDefaultRegistration<T> defaultRegistration)
else if (registration is IRegistrationBase<T> defaultRegistration)
{
if (defaultRegistration.Lifestyle == Lifestyle.Singleton)
resolvedInstance = GetOrCreateSingletonInstance(defaultRegistration, arguments, resolveStack);
@ -242,7 +242,7 @@ namespace LightweightIocContainer
/// <param name="arguments">The arguments to resolve</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <returns>An existing or newly created singleton instance of the given <see cref="Type"/></returns>
private T GetOrCreateSingletonInstance<T>(IDefaultRegistration<T> registration, object[] arguments, List<Type> resolveStack)
private T GetOrCreateSingletonInstance<T>(IRegistrationBase<T> registration, object[] arguments, List<Type> resolveStack)
{
//if a singleton instance exists return it
object instance = _singletons.FirstOrDefault(s => s.type == typeof(T)).instance;
@ -306,10 +306,30 @@ namespace LightweightIocContainer
/// <param name="arguments">The constructor arguments</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <returns>A newly created instance of the given <see cref="Type"/></returns>
private T CreateInstance<T>(IDefaultRegistration<T> registration, object[] arguments, List<Type> resolveStack)
private T CreateInstance<T>(IRegistrationBase<T> registration, object[] arguments, List<Type> resolveStack)
{
arguments = ResolveConstructorArguments(registration.ImplementationType, arguments, resolveStack);
T instance = (T) Activator.CreateInstance(registration.ImplementationType, arguments);
T instance;
if (registration is IDefaultRegistration<T> defaultRegistration)
{
arguments = ResolveConstructorArguments(defaultRegistration.ImplementationType, arguments, resolveStack);
instance = (T) Activator.CreateInstance(defaultRegistration.ImplementationType, arguments);
}
else if (registration is ISingleTypeRegistration<T> 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;

@ -456,6 +456,24 @@
<param name="action">The <see cref="T:System.Action`1"/></param>
<returns>The current instance of this <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/></returns>
</member>
<member name="T:LightweightIocContainer.Interfaces.Registrations.ISingleTypeRegistration`1">
<summary>
The <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/> to register either only an interface or only a <see cref="T:System.Type"/>
</summary>
<typeparam name="T">The <see cref="T:System.Type"/> of the <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/></typeparam>
</member>
<member name="P:LightweightIocContainer.Interfaces.Registrations.ISingleTypeRegistration`1.FactoryMethod">
<summary>
<see cref="T:System.Func`2"/> that is invoked instead of creating an instance of this <see cref="T:System.Type"/> the default way
</summary>
</member>
<member name="M:LightweightIocContainer.Interfaces.Registrations.ISingleTypeRegistration`1.WithFactoryMethod(System.Func{LightweightIocContainer.Interfaces.IIocContainer,`0})">
<summary>
Pass a <see cref="T:System.Func`2"/> that will be invoked instead of creating an instance of this <see cref="T:System.Type"/> the default way
</summary>
<param name="factoryMethod">The <see cref="T:System.Func`2"/></param>
<returns>The current instance of this <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/></returns>
</member>
<member name="T:LightweightIocContainer.Interfaces.Registrations.ITypedFactoryRegistration`1">
<summary>
The registration that is used to register an abstract typed factory
@ -579,7 +597,7 @@
<exception cref="T:LightweightIocContainer.Exceptions.TypeNotRegisteredException">The given <see cref="T:System.Type"/> is not registered in this <see cref="T:LightweightIocContainer.IocContainer"/></exception>
<exception cref="T:LightweightIocContainer.Exceptions.UnknownRegistrationException">The registration for the given <see cref="T:System.Type"/> has an unknown <see cref="T:System.Type"/></exception>
</member>
<member name="M:LightweightIocContainer.IocContainer.GetOrCreateSingletonInstance``1(LightweightIocContainer.Interfaces.Registrations.IDefaultRegistration{``0},System.Object[],System.Collections.Generic.List{System.Type})">
<member name="M:LightweightIocContainer.IocContainer.GetOrCreateSingletonInstance``1(LightweightIocContainer.Interfaces.Registrations.IRegistrationBase{``0},System.Object[],System.Collections.Generic.List{System.Type})">
<summary>
Gets or creates a singleton instance of a given <see cref="T:System.Type"/>
</summary>
@ -601,7 +619,7 @@
<exception cref="T:LightweightIocContainer.Exceptions.MultitonResolveException">No arguments given</exception>
<exception cref="T:LightweightIocContainer.Exceptions.MultitonResolveException">Scope argument not given</exception>
</member>
<member name="M:LightweightIocContainer.IocContainer.CreateInstance``1(LightweightIocContainer.Interfaces.Registrations.IDefaultRegistration{``0},System.Object[],System.Collections.Generic.List{System.Type})">
<member name="M:LightweightIocContainer.IocContainer.CreateInstance``1(LightweightIocContainer.Interfaces.Registrations.IRegistrationBase{``0},System.Object[],System.Collections.Generic.List{System.Type})">
<summary>
Creates an instance of a given <see cref="T:System.Type"/>
</summary>
@ -759,11 +777,11 @@
</member>
<member name="M:LightweightIocContainer.Registrations.RegistrationFactory.Register``1(LightweightIocContainer.Lifestyle)">
<summary>
Register a <see cref="T:System.Type"/> without an interface and create a <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/>
Register a <see cref="T:System.Type"/> without an interface and create a <see cref="T:LightweightIocContainer.Interfaces.Registrations.ISingleTypeRegistration`1"/>
</summary>
<typeparam name="TImplementation">The <see cref="T:System.Type"/> to register</typeparam>
<param name="lifestyle">The <see cref="T:LightweightIocContainer.Lifestyle"/> for this <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/></param>
<returns>A new created <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/> with the given parameters</returns>
<typeparam name="T">The <see cref="T:System.Type"/> to register</typeparam>
<param name="lifestyle">The <see cref="T:LightweightIocContainer.Lifestyle"/> for this <see cref="T:LightweightIocContainer.Interfaces.Registrations.ISingleTypeRegistration`1"/></param>
<returns>A new created <see cref="T:LightweightIocContainer.Interfaces.Registrations.ISingleTypeRegistration`1"/> with the given parameters</returns>
</member>
<member name="M:LightweightIocContainer.Registrations.RegistrationFactory.Register``3">
<summary>
@ -781,6 +799,31 @@
<typeparam name="TFactory">The abstract typed factory to register</typeparam>
<returns>A new created <see cref="T:LightweightIocContainer.Interfaces.Registrations.ITypedFactoryRegistration`1"/> with the given parameters</returns>
</member>
<member name="T:LightweightIocContainer.Registrations.SingleTypeRegistration`1">
<summary>
The <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/> to register either only an interface or only a <see cref="T:System.Type"/>
</summary>
<typeparam name="T">The <see cref="T:System.Type"/> of the <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/></typeparam>
</member>
<member name="M:LightweightIocContainer.Registrations.SingleTypeRegistration`1.#ctor(System.Type,LightweightIocContainer.Lifestyle)">
<summary>
The <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/> to register either only an interface or only a <see cref="T:System.Type"/>
</summary>
<param name="interfaceType">The <see cref="T:System.Type"/> of the interface or <see cref="T:System.Type"/></param>
<param name="lifestyle">The <see cref="T:LightweightIocContainer.Lifestyle"/> of the <see cref="T:LightweightIocContainer.Registrations.RegistrationBase`1"/></param>
</member>
<member name="P:LightweightIocContainer.Registrations.SingleTypeRegistration`1.FactoryMethod">
<summary>
<see cref="T:System.Func`2"/> that is invoked instead of creating an instance of this <see cref="T:System.Type"/> the default way
</summary>
</member>
<member name="M:LightweightIocContainer.Registrations.SingleTypeRegistration`1.WithFactoryMethod(System.Func{LightweightIocContainer.Interfaces.IIocContainer,`0})">
<summary>
Pass a <see cref="T:System.Func`2"/> that will be invoked instead of creating an instance of this <see cref="T:System.Type"/> the default way
</summary>
<param name="factoryMethod">The <see cref="T:System.Func`2"/></param>
<returns>The current instance of this <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase`1"/></returns>
</member>
<member name="T:LightweightIocContainer.Registrations.TypedFactoryRegistration`1">
<summary>
The registration that is used to register an abstract typed factory

@ -35,17 +35,14 @@ namespace LightweightIocContainer.Registrations
}
/// <summary>
/// Register a <see cref="Type"/> without an interface and create a <see cref="IRegistrationBase{TInterface}"/>
/// Register a <see cref="Type"/> without an interface and create a <see cref="ISingleTypeRegistration{TInterface}"/>
/// </summary>
/// <typeparam name="TImplementation">The <see cref="Type"/> to register</typeparam>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="IRegistrationBase{TInterface}"/></param>
/// <returns>A new created <see cref="IRegistrationBase{TInterface}"/> with the given parameters</returns>
public IRegistrationBase<TImplementation> Register<TImplementation>(Lifestyle lifestyle)
/// <typeparam name="T">The <see cref="Type"/> to register</typeparam>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="ISingleTypeRegistration{TInterface}"/></param>
/// <returns>A new created <see cref="ISingleTypeRegistration{TInterface}"/> with the given parameters</returns>
public ISingleTypeRegistration<T> Register<T>(Lifestyle lifestyle)
{
if (typeof(TImplementation).IsInterface)
throw new InvalidRegistrationException("Can't register an interface without its implementation type.");
return Register<TImplementation, TImplementation>(lifestyle);
return new SingleTypeRegistration<T>(typeof(T), lifestyle);
}
/// <summary>

@ -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
{
/// <summary>
/// The <see cref="IRegistrationBase{TInterface}"/> to register either only an interface or only a <see cref="Type"/>
/// </summary>
/// <typeparam name="T">The <see cref="Type"/> of the <see cref="IRegistrationBase{TInterface}"/></typeparam>
public class SingleTypeRegistration<T> : RegistrationBase<T>, ISingleTypeRegistration<T>
{
/// <summary>
/// The <see cref="IRegistrationBase{TInterface}"/> to register either only an interface or only a <see cref="Type"/>
/// </summary>
/// <param name="interfaceType">The <see cref="Type"/> of the interface or <see cref="Type"/></param>
/// <param name="lifestyle">The <see cref="Lifestyle"/> of the <see cref="RegistrationBase{TInterface}"/></param>
public SingleTypeRegistration(Type interfaceType, Lifestyle lifestyle)
: base(interfaceType, lifestyle)
{
Name = $"{InterfaceType.Name}, Lifestyle: {Lifestyle.ToString()}";
}
/// <summary>
/// <see cref="Func{T,TResult}"/> that is invoked instead of creating an instance of this <see cref="Type"/> the default way
/// </summary>
public Func<IIocContainer, T> FactoryMethod { get; private set; }
/// <summary>
/// Pass a <see cref="Func{T,TResult}"/> that will be invoked instead of creating an instance of this <see cref="Type"/> the default way
/// </summary>
/// <param name="factoryMethod">The <see cref="Func{T,TResult}"/></param>
/// <returns>The current instance of this <see cref="IRegistrationBase{TInterface}"/></returns>
public IRegistrationBase<T> WithFactoryMethod(Func<IIocContainer, T> factoryMethod)
{
FactoryMethod = factoryMethod;
return this;
}
}
}

@ -166,12 +166,6 @@ namespace Test.LightweightIocContainer
Assert.AreEqual(typeof(ITest), exception.Type);
}
[Test]
public void TestRegisterInterfaceWithoutImplementation()
{
Assert.Throws<InvalidRegistrationException>(() => _iocContainer.Register<ITest>());
}
[Test]
public void TestRegisterFactoryWithoutCreate()
{
@ -217,6 +211,13 @@ namespace Test.LightweightIocContainer
Assert.IsInstanceOf<Test>(resolvedTest);
}
[Test]
public void TestResolveInterfaceWithoutImplementation()
{
_iocContainer.Register<ITest>();
Assert.Throws<InvalidRegistrationException>(() => _iocContainer.Resolve<ITest>());
}
[Test]
public void TestResolveWithParams()
{

Loading…
Cancel
Save