#6: start refactoring `Injector` to Ioc

pull/32/head
Simon Gockner 7 years ago
parent a805731d13
commit 77e158f564
  1. 2
      LightweightIocContainer/Exceptions/InternalResolveException.cs
  2. 4
      LightweightIocContainer/Exceptions/TypeNotRegisteredException.cs
  3. 12
      LightweightIocContainer/Interfaces/IIocContainer.cs
  4. 10
      LightweightIocContainer/Interfaces/IIocInstaller.cs
  5. 2
      LightweightIocContainer/Interfaces/Registrations/IDefaultRegistration.cs
  6. 16
      LightweightIocContainer/IocContainer.cs
  7. 2
      LightweightIocContainer/Registrations/DefaultRegistration.cs
  8. 6
      LightweightIocContainer/Registrations/RegistrationFactory.cs
  9. 14
      LightweightIocContainer/Registrations/TypedFactoryRegistration.cs
  10. 189
      Test.LightweightIocContainer/InjectorContainerTest.cs
  11. 189
      Test.LightweightIocContainer/IocContainerTest.cs

@ -8,7 +8,7 @@ using LightweightIocContainer.Interfaces;
namespace LightweightIocContainer.Exceptions
{
/// <summary>
/// An internal Error happened while the <see cref="IInjectorContainer"/> tried to resolve an instance
/// An internal Error happened while the <see cref="IIocContainer"/> tried to resolve an instance
/// </summary>
public class InternalResolveException : Exception
{

@ -8,12 +8,12 @@ using LightweightIocContainer.Interfaces;
namespace LightweightIocContainer.Exceptions
{
/// <summary>
/// The Type is not registered in this <see cref="IInjectorContainer"/>
/// The Type is not registered in this <see cref="IIocContainer"/>
/// </summary>
public class TypeNotRegisteredException : Exception
{
public TypeNotRegisteredException(Type type)
: base($"Type {type.Name} is not registered in this InjectorContainer.")
: base($"Type {type.Name} is not registered in this IocContainer.")
{
Type = type;
}

@ -10,17 +10,17 @@ namespace LightweightIocContainer.Interfaces
/// <summary>
/// The main container that carries all the <see cref="IRegistrationBase"/>s and can resolve all the types you'll ever want
/// </summary>
public interface IInjectorContainer : IDisposable
public interface IIocContainer : IDisposable
{
/// <summary>
/// Install the given installers for the current <see cref="IInjectorContainer"/>
/// Install the given installers for the current <see cref="IIocContainer"/>
/// </summary>
/// <param name="installers">The given <see cref="IInjectorInstaller"/>s</param>
/// <returns>An instance of the current <see cref="IInjectorContainer"/></returns>
IInjectorContainer Install(params IInjectorInstaller[] installers);
/// <param name="installers">The given <see cref="IIocInstaller"/>s</param>
/// <returns>An instance of the current <see cref="IIocContainer"/></returns>
IIocContainer Install(params IIocInstaller[] installers);
/// <summary>
/// Add the <see cref="IRegistrationBase"/> to the the <see cref="IInjectorContainer"/>
/// Add the <see cref="IRegistrationBase"/> to the the <see cref="IIocContainer"/>
/// </summary>
/// <param name="registration">The given <see cref="IRegistrationBase"/></param>
void Register(IRegistrationBase registration);

@ -7,14 +7,14 @@ using LightweightIocContainer.Interfaces.Registrations;
namespace LightweightIocContainer.Interfaces
{
/// <summary>
/// The base class for <see cref="IInjectorContainer"/> installers
/// The base class for <see cref="IIocContainer"/> installers
/// </summary>
public interface IInjectorInstaller
public interface IIocInstaller
{
/// <summary>
/// Install the needed <see cref="IRegistrationBase"/>s in the given <see cref="IInjectorContainer"/>
/// Install the needed <see cref="IRegistrationBase"/>s in the given <see cref="IIocContainer"/>
/// </summary>
/// <param name="container">The current <see cref="IInjectorContainer"/></param>
void Install(IInjectorContainer container);
/// <param name="container">The current <see cref="IIocContainer"/></param>
void Install(IIocContainer container);
}
}

@ -24,7 +24,7 @@ namespace LightweightIocContainer.Interfaces.Registrations
/// <summary>
/// This action is invoked when an instance of this type is created.
/// <para>Can be set in the <see cref="IInjectorInstaller"/> by calling <see cref="OnCreate"/></para>
/// <para>Can be set in the <see cref="IIocInstaller"/> by calling <see cref="OnCreate"/></para>
/// </summary>
Action<TInterface> OnCreateAction { get; }

@ -15,17 +15,17 @@ namespace LightweightIocContainer
/// <summary>
/// The main container that carries all the <see cref="IRegistrationBase"/>s and can resolve all the types you'll ever want
/// </summary>
public class InjectorContainer : IInjectorContainer
public class IocContainer : IIocContainer
{
private readonly List<IRegistrationBase> _registrations = new List<IRegistrationBase>();
private readonly List<(Type type, object instance)> _singletons = new List<(Type, object)>(); //TODO: Think about the usage of ConditionalWeakTable<>
/// <summary>
/// Install the given installers for the current <see cref="InjectorContainer"/>
/// Install the given installers for the current <see cref="IocContainer"/>
/// </summary>
/// <param name="installers">The given <see cref="IInjectorInstaller"/>s</param>
/// <returns>An instance of the current <see cref="InjectorContainer"/></returns>
public IInjectorContainer Install(params IInjectorInstaller[] installers)
/// <param name="installers">The given <see cref="IIocInstaller"/>s</param>
/// <returns>An instance of the current <see cref="IocContainer"/></returns>
public IIocContainer Install(params IIocInstaller[] installers)
{
foreach (var installer in installers)
{
@ -36,7 +36,7 @@ namespace LightweightIocContainer
}
/// <summary>
/// Add the <see cref="IRegistrationBase"/> to the the <see cref="InjectorContainer"/>
/// Add the <see cref="IRegistrationBase"/> to the the <see cref="IocContainer"/>
/// </summary>
/// <param name="registration">The given <see cref="IRegistrationBase"/></param>
public void Register(IRegistrationBase registration)
@ -74,7 +74,7 @@ namespace LightweightIocContainer
/// <exception cref="InternalResolveException">Could not find function <see cref="ResolveInternal{T}"/></exception>
public object Resolve(Type type, object[] arguments) //somehow the order of the arguments is different in the application compared to the unit test
{
var resolveMethod = typeof(InjectorContainer).GetMethod(nameof(ResolveInternal), BindingFlags.NonPublic | BindingFlags.Instance);
var resolveMethod = typeof(IocContainer).GetMethod(nameof(ResolveInternal), BindingFlags.NonPublic | BindingFlags.Instance);
var genericResolveMethod = resolveMethod?.MakeGenericMethod(type);
if (genericResolveMethod == null)
@ -89,7 +89,7 @@ namespace LightweightIocContainer
/// <typeparam name="T">The registered type</typeparam>
/// <param name="arguments">The constructor arguments</param>
/// <returns>An instance of the given registered type</returns>
/// <exception cref="TypeNotRegisteredException">The given type is not registered in this <see cref="InjectorContainer"/></exception>
/// <exception cref="TypeNotRegisteredException">The given type is not registered in this <see cref="IocContainer"/></exception>
/// <exception cref="UnknownRegistrationException">The registration for the given type has an unknown type</exception>
private T ResolveInternal<T>(params object[] arguments)
{

@ -46,7 +46,7 @@ namespace LightweightIocContainer.Registrations
/// <summary>
/// This action is invoked when an instance of this type is created.
/// <para>Can be set in the <see cref="IInjectorInstaller"/> by calling <see cref="OnCreate"/></para>
/// <para>Can be set in the <see cref="IIocInstaller"/> by calling <see cref="OnCreate"/></para>
/// </summary>
public Action<TInterface> OnCreateAction { get; private set; }

@ -8,7 +8,7 @@ using LightweightIocContainer.Interfaces.Registrations;
namespace LightweightIocContainer.Registrations
{
/// <summary>
/// A factory to register interfaces and factories in an <see cref="IInjectorInstaller"/> and create the needed <see cref="IRegistrationBase"/>s
/// A factory to register interfaces and factories in an <see cref="IIocInstaller"/> and create the needed <see cref="IRegistrationBase"/>s
/// </summary>
public static class RegistrationFactory
{
@ -28,9 +28,9 @@ namespace LightweightIocContainer.Registrations
/// Register an Interface as an abstract typed factory and create a <see cref="ITypedFactoryRegistration{TFactory}"/>
/// </summary>
/// <typeparam name="TFactory">The abstract typed factory to register</typeparam>
/// <param name="container">The current <see cref="IInjectorContainer"/></param>
/// <param name="container">The current <see cref="IIocContainer"/></param>
/// <returns>A new created <see cref="ITypedFactoryRegistration{TFactory}"/> with the given parameters</returns>
public static ITypedFactoryRegistration<TFactory> RegisterFactory<TFactory>(IInjectorContainer container) //TODO: Find a nicer way to inject the container into `TypedFactoryRegistration`
public static ITypedFactoryRegistration<TFactory> RegisterFactory<TFactory>(IIocContainer container) //TODO: Find a nicer way to inject the container into `TypedFactoryRegistration`
{
return new TypedFactoryRegistration<TFactory>(typeof(TFactory), container);
}

@ -20,9 +20,9 @@ namespace LightweightIocContainer.Registrations
/// <typeparam name="TFactory">The type of the abstract typed factory</typeparam>
public class TypedFactoryRegistration<TFactory> : ITypedFactoryRegistration<TFactory>
{
private readonly IInjectorContainer _container;
private readonly IIocContainer _container;
public TypedFactoryRegistration(Type factoryType, IInjectorContainer container)
public TypedFactoryRegistration(Type factoryType, IIocContainer container)
{
_container = container;
@ -69,11 +69,11 @@ namespace LightweightIocContainer.Registrations
typeBuilder.AddInterfaceImplementation(InterfaceType);
//add `private readonly IInjectorContainer _container` field
FieldBuilder containerFieldBuilder = typeBuilder.DefineField("_container", typeof(IInjectorContainer), FieldAttributes.Private | FieldAttributes.InitOnly);
//add `private readonly IIocContainer _container` field
FieldBuilder containerFieldBuilder = typeBuilder.DefineField("_container", typeof(IIocContainer), FieldAttributes.Private | FieldAttributes.InitOnly);
//add ctor
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new[] {typeof(IInjectorContainer)});
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new[] {typeof(IIocContainer)});
var constructorGenerator = constructorBuilder.GetILGenerator();
constructorGenerator.Emit(OpCodes.Ldarg_0);
constructorGenerator.Emit(OpCodes.Ldarg_1);
@ -85,7 +85,7 @@ namespace LightweightIocContainer.Registrations
//create a method that looks like this
//public `createMethod.ReturnType` Create(`createMethod.GetParameters()`)
//{
// return IInjectorContainer.Resolve(`createMethod.ReturnType`, params);
// return IIocContainer.Resolve(`createMethod.ReturnType`, params);
//}
var args = createMethod.GetParameters();
@ -122,7 +122,7 @@ namespace LightweightIocContainer.Registrations
generator.EmitCall(OpCodes.Call, emptyArray, null);
}
generator.EmitCall(OpCodes.Callvirt, typeof(IInjectorContainer).GetMethod(nameof(IInjectorContainer.Resolve), new[] { typeof(Type), typeof(object[])}), null);
generator.EmitCall(OpCodes.Callvirt, typeof(IIocContainer).GetMethod(nameof(IIocContainer.Resolve), new[] { typeof(Type), typeof(object[])}), null);
generator.Emit(OpCodes.Castclass, createMethod.ReturnType);
generator.Emit(OpCodes.Ret);
}

@ -1,189 +0,0 @@
using LightweightIocContainer;
using LightweightIocContainer.Exceptions;
using LightweightIocContainer.Interfaces;
using LightweightIocContainer.Registrations;
using Moq;
using NUnit.Framework;
namespace Test.LightweightIocContainer
{
[TestFixture]
public class InjectorContainerTest
{
#region TestClasses
//some of the test classes have to be public to allow the implementation of the factory
public interface ITest
{
}
public interface ITestFactory
{
ITest Create();
ITest Create(string name);
}
private class Test : ITest
{
}
private class TestConstructor : ITest
{
public TestConstructor(string name, Test test)
{
}
}
#endregion
[Test]
public void TestInstall()
{
Mock<IInjectorInstaller> installerMock = new Mock<IInjectorInstaller>();
IInjectorContainer injectorContainer = new InjectorContainer();
IInjectorContainer returnedContainer = injectorContainer.Install(installerMock.Object);
installerMock.Verify(m => m.Install(It.IsAny<IInjectorContainer>()), Times.Once);
Assert.AreEqual(injectorContainer, returnedContainer);
}
[Test]
public void TestInstallMultiple()
{
Mock<IInjectorInstaller> installer1Mock = new Mock<IInjectorInstaller>();
Mock<IInjectorInstaller> installer2Mock = new Mock<IInjectorInstaller>();
Mock<IInjectorInstaller> installer3Mock = new Mock<IInjectorInstaller>();
IInjectorContainer injectorContainer = new InjectorContainer();
IInjectorContainer returnedContainer = injectorContainer.Install(installer1Mock.Object, installer2Mock.Object, installer3Mock.Object);
installer1Mock.Verify(m => m.Install(It.IsAny<IInjectorContainer>()), Times.Once);
installer2Mock.Verify(m => m.Install(It.IsAny<IInjectorContainer>()), Times.Once);
installer3Mock.Verify(m => m.Install(It.IsAny<IInjectorContainer>()), Times.Once);
Assert.AreEqual(injectorContainer, returnedContainer);
}
[Test]
public void TestResolveNotRegistered()
{
IInjectorContainer injectorContainer = new InjectorContainer();
Assert.Throws<TypeNotRegisteredException>(() => injectorContainer.Resolve<ITest>());
}
[Test]
public void TestResolve()
{
IInjectorContainer injectorContainer = new InjectorContainer();
injectorContainer.Register(RegistrationFactory.Register<ITest, Test>());
ITest resolvedTest = injectorContainer.Resolve<ITest>();
Assert.IsInstanceOf<Test>(resolvedTest);
}
[Test]
public void TestResolveWithParams()
{
IInjectorContainer injectorContainer = new InjectorContainer();
injectorContainer.Register(RegistrationFactory.Register<ITest, TestConstructor>());
ITest resolvedTest = injectorContainer.Resolve<ITest>("Test", new Test());
Assert.IsInstanceOf<TestConstructor>(resolvedTest);
}
[Test]
public void TestResolveWithMissingParam()
{
IInjectorContainer injectorContainer = new InjectorContainer();
injectorContainer.Register(RegistrationFactory.Register<ITest, TestConstructor>());
injectorContainer.Register(RegistrationFactory.Register<Test, Test>()); //this registration is abnormal and should only be used in unit tests
ITest resolvedTest = injectorContainer.Resolve<ITest>("Test");
Assert.IsInstanceOf<TestConstructor>(resolvedTest);
}
[Test]
public void TestResolveReflection()
{
IInjectorContainer injectorContainer = new InjectorContainer();
injectorContainer.Register(RegistrationFactory.Register<ITest, Test>());
object resolvedTest = injectorContainer.Resolve(typeof(ITest), null);
Assert.IsInstanceOf<Test>(resolvedTest);
}
[Test]
public void TestResolveSingleton()
{
IInjectorContainer injectorContainer = new InjectorContainer();
injectorContainer.Register(RegistrationFactory.Register<ITest, Test>(Lifestyle.Singleton));
ITest resolvedTest = injectorContainer.Resolve<ITest>();
ITest secondResolvedTest = injectorContainer.Resolve<ITest>();
Assert.AreEqual(resolvedTest, secondResolvedTest);
}
[Test]
public void TestResolveTransient()
{
IInjectorContainer injectorContainer = new InjectorContainer();
injectorContainer.Register(RegistrationFactory.Register<ITest, Test>());
ITest resolvedTest = injectorContainer.Resolve<ITest>();
ITest secondResolvedTest = injectorContainer.Resolve<ITest>();
Assert.AreNotEqual(resolvedTest, secondResolvedTest);
}
[Test]
public void TestResolveFactory()
{
IInjectorContainer injectorContainer = new InjectorContainer();
injectorContainer.Register(RegistrationFactory.Register<ITest, Test>());
injectorContainer.Register(RegistrationFactory.RegisterFactory<ITestFactory>(injectorContainer));
ITestFactory testFactory = injectorContainer.Resolve<ITestFactory>();
Assert.IsInstanceOf<ITestFactory>(testFactory);
}
[Test]
public void TestResolveFromFactory()
{
IInjectorContainer injectorContainer = new InjectorContainer();
injectorContainer.Register(RegistrationFactory.Register<ITest, Test>());
injectorContainer.Register(RegistrationFactory.RegisterFactory<ITestFactory>(injectorContainer));
ITestFactory testFactory = injectorContainer.Resolve<ITestFactory>();
ITest createdTest = testFactory.Create();
Assert.IsInstanceOf<Test>(createdTest);
}
[Test]
public void TestResolveFromFactoryWithParams()
{
IInjectorContainer injectorContainer = new InjectorContainer();
injectorContainer.Register(RegistrationFactory.Register<ITest, TestConstructor>());
injectorContainer.Register(RegistrationFactory.Register<Test, Test>()); //this registration is abnormal and should only be used in unit tests
injectorContainer.Register(RegistrationFactory.RegisterFactory<ITestFactory>(injectorContainer));
ITestFactory testFactory = injectorContainer.Resolve<ITestFactory>();
ITest createdTest = testFactory.Create("Test");
Assert.IsInstanceOf<TestConstructor>(createdTest);
}
}
}

@ -0,0 +1,189 @@
using LightweightIocContainer;
using LightweightIocContainer.Exceptions;
using LightweightIocContainer.Interfaces;
using LightweightIocContainer.Registrations;
using Moq;
using NUnit.Framework;
namespace Test.LightweightIocContainer
{
[TestFixture]
public class IocContainerTest
{
#region TestClasses
//some of the test classes have to be public to allow the implementation of the factory
public interface ITest
{
}
public interface ITestFactory
{
ITest Create();
ITest Create(string name);
}
private class Test : ITest
{
}
private class TestConstructor : ITest
{
public TestConstructor(string name, Test test)
{
}
}
#endregion
[Test]
public void TestInstall()
{
Mock<IIocInstaller> installerMock = new Mock<IIocInstaller>();
IIocContainer iocContainer = new IocContainer();
IIocContainer returnedContainer = iocContainer.Install(installerMock.Object);
installerMock.Verify(m => m.Install(It.IsAny<IIocContainer>()), Times.Once);
Assert.AreEqual(iocContainer, returnedContainer);
}
[Test]
public void TestInstallMultiple()
{
Mock<IIocInstaller> installer1Mock = new Mock<IIocInstaller>();
Mock<IIocInstaller> installer2Mock = new Mock<IIocInstaller>();
Mock<IIocInstaller> installer3Mock = new Mock<IIocInstaller>();
IIocContainer iocContainer = new IocContainer();
IIocContainer returnedContainer = iocContainer.Install(installer1Mock.Object, installer2Mock.Object, installer3Mock.Object);
installer1Mock.Verify(m => m.Install(It.IsAny<IIocContainer>()), Times.Once);
installer2Mock.Verify(m => m.Install(It.IsAny<IIocContainer>()), Times.Once);
installer3Mock.Verify(m => m.Install(It.IsAny<IIocContainer>()), Times.Once);
Assert.AreEqual(iocContainer, returnedContainer);
}
[Test]
public void TestResolveNotRegistered()
{
IIocContainer iocContainer = new IocContainer();
Assert.Throws<TypeNotRegisteredException>(() => iocContainer.Resolve<ITest>());
}
[Test]
public void TestResolve()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test>());
ITest resolvedTest = iocContainer.Resolve<ITest>();
Assert.IsInstanceOf<Test>(resolvedTest);
}
[Test]
public void TestResolveWithParams()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, TestConstructor>());
ITest resolvedTest = iocContainer.Resolve<ITest>("Test", new Test());
Assert.IsInstanceOf<TestConstructor>(resolvedTest);
}
[Test]
public void TestResolveWithMissingParam()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, TestConstructor>());
iocContainer.Register(RegistrationFactory.Register<Test, Test>()); //this registration is abnormal and should only be used in unit tests
ITest resolvedTest = iocContainer.Resolve<ITest>("Test");
Assert.IsInstanceOf<TestConstructor>(resolvedTest);
}
[Test]
public void TestResolveReflection()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test>());
object resolvedTest = iocContainer.Resolve(typeof(ITest), null);
Assert.IsInstanceOf<Test>(resolvedTest);
}
[Test]
public void TestResolveSingleton()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test>(Lifestyle.Singleton));
ITest resolvedTest = iocContainer.Resolve<ITest>();
ITest secondResolvedTest = iocContainer.Resolve<ITest>();
Assert.AreEqual(resolvedTest, secondResolvedTest);
}
[Test]
public void TestResolveTransient()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test>());
ITest resolvedTest = iocContainer.Resolve<ITest>();
ITest secondResolvedTest = iocContainer.Resolve<ITest>();
Assert.AreNotEqual(resolvedTest, secondResolvedTest);
}
[Test]
public void TestResolveFactory()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test>());
iocContainer.Register(RegistrationFactory.RegisterFactory<ITestFactory>(iocContainer));
ITestFactory testFactory = iocContainer.Resolve<ITestFactory>();
Assert.IsInstanceOf<ITestFactory>(testFactory);
}
[Test]
public void TestResolveFromFactory()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test>());
iocContainer.Register(RegistrationFactory.RegisterFactory<ITestFactory>(iocContainer));
ITestFactory testFactory = iocContainer.Resolve<ITestFactory>();
ITest createdTest = testFactory.Create();
Assert.IsInstanceOf<Test>(createdTest);
}
[Test]
public void TestResolveFromFactoryWithParams()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, TestConstructor>());
iocContainer.Register(RegistrationFactory.Register<Test, Test>()); //this registration is abnormal and should only be used in unit tests
iocContainer.Register(RegistrationFactory.RegisterFactory<ITestFactory>(iocContainer));
ITestFactory testFactory = iocContainer.Resolve<ITestFactory>();
ITest createdTest = testFactory.Create("Test");
Assert.IsInstanceOf<TestConstructor>(createdTest);
}
}
}
Loading…
Cancel
Save