- add multiton lifestyle (#3)

- update tests
- bump c# language version to 7.3
pull/32/head
Simon Gockner 7 years ago
parent f55a05a5c9
commit c999234911
  1. 4
      LightweightIocContainer.sln.DotSettings
  2. 25
      LightweightIocContainer/Exceptions/MultitonResolveException.cs
  3. 20
      LightweightIocContainer/Interfaces/Registrations/IMultitonRegistration.cs
  4. 44
      LightweightIocContainer/IocContainer.cs
  5. 7
      LightweightIocContainer/Lifestyle.cs
  6. 3
      LightweightIocContainer/LightweightIocContainer.csproj
  7. 27
      LightweightIocContainer/Registrations/MultitonRegistration.cs
  8. 12
      LightweightIocContainer/Registrations/RegistrationFactory.cs
  9. 91
      Test.LightweightIocContainer/IocContainerTest.cs

@ -2,4 +2,6 @@
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">// Author: $USER_NAME$&#xD;
// Created: $CREATED_YEAR$-$CREATED_MONTH$-$CREATED_DAY$&#xD;
// Copyright(c) $CREATED_YEAR$ SimonG. All Rights Reserved.</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Method/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Method/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=gockner/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=multiton/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

@ -0,0 +1,25 @@
// // Author: Gockner, Simon
// // Created: 2019-06-07
// // Copyright(c) 2019 SimonG. All Rights Reserved.
using System;
namespace LightweightIocContainer.Exceptions
{
/// <summary>
/// An error happened while trying to resolve a multiton
/// </summary>
public class MultitonResolveException : InternalResolveException
{
public MultitonResolveException(string message, Type type)
: base(message)
{
Type = type;
}
/// <summary>
/// The type of the multiton that's responsible for the exception
/// </summary>
public Type Type { get; }
}
}

@ -0,0 +1,20 @@
// // Author: Gockner, Simon
// // Created: 2019-06-07
// // Copyright(c) 2019 SimonG. All Rights Reserved.
using System;
namespace LightweightIocContainer.Interfaces.Registrations
{
/// <summary>
/// The registration that is used to register a multiton
/// </summary>
/// <typeparam name="TInterface">The registered interface</typeparam>
public interface IMultitonRegistration<TInterface> : IDefaultRegistration<TInterface>
{
/// <summary>
/// The type of the multiton scope
/// </summary>
Type Scope { get; }
}
}

@ -19,6 +19,7 @@ namespace LightweightIocContainer
{
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<>
private readonly List<(Type type, Type scope, List<(object scopeInstance, object instance)> instances)> _multitons = new List<(Type, Type, List<(object, object)>)>();
/// <summary>
/// Install the given installers for the current <see cref="IocContainer"/>
@ -106,6 +107,8 @@ namespace LightweightIocContainer
{
if (defaultRegistration.Lifestyle == Lifestyle.Singleton)
return GetOrCreateSingletonInstance<T>(defaultRegistration, arguments);
else if (defaultRegistration is IMultitonRegistration<T> multitonRegistration && defaultRegistration.Lifestyle == Lifestyle.Multiton)
return GetOrCreateMultitonInstance<T>(multitonRegistration, arguments);
return CreateInstance<T>(defaultRegistration, arguments);
}
@ -122,7 +125,7 @@ namespace LightweightIocContainer
/// </summary>
/// <typeparam name="T">The given type</typeparam>
/// <param name="registration">The registration of the given type</param>
/// <param name="arguments">The constructor arguments</param>
/// <param name="arguments">The arguments to resolve</param>
/// <returns>An existing or newly created singleton instance of the given type</returns>
private T GetOrCreateSingletonInstance<T>(IDefaultRegistration<T> registration, params object[] arguments)
{
@ -138,6 +141,44 @@ namespace LightweightIocContainer
return newInstance;
}
/// <summary>
/// Gets or creates a multiton instance of a given type
/// </summary>
/// <typeparam name="T">The given type</typeparam>
/// <param name="registration">The registration of the given type</param>
/// <param name="arguments">The arguments to resolve</param>
/// <returns>An existing or newly created multiton instance of the given type</returns>
/// <exception cref="MultitonResolveException">No arguments given</exception>
/// <exception cref="MultitonResolveException">Scope argument not given</exception>
private T GetOrCreateMultitonInstance<T>(IMultitonRegistration<T> registration, params object[] arguments)
{
if (arguments == null || !arguments.Any())
throw new MultitonResolveException("Can not resolve multiton without arguments.", typeof(T));
object scopeArgument = arguments[0];
if (scopeArgument.GetType() != registration.Scope)
throw new MultitonResolveException($"Can not resolve multiton without the first argument being the scope (should be of type {registration.Scope}).", typeof(T));
//if a multiton for the given scope exists return it
var instances = _multitons.FirstOrDefault(m => m.type == typeof(T) && m.scope == registration.Scope).instances;
if (instances != null && instances.Any())
{
var instance = instances.FirstOrDefault(i => i.scopeInstance.Equals(scopeArgument));
if (instance != (null, null))
return (T) instance.instance;
T createdInstance = CreateInstance<T>(registration, arguments);
instances.Add((scopeArgument, createdInstance));
return createdInstance;
}
T newInstance = CreateInstance<T>(registration, arguments);
_multitons.Add((typeof(T), registration.Scope, new List<(object, object)>() {(scopeArgument, newInstance)}));
return newInstance;
}
/// <summary>
/// Creates an instance of a given type
/// </summary>
@ -206,6 +247,7 @@ namespace LightweightIocContainer
{
_registrations.Clear();
_singletons.Clear();
_multitons.Clear();
}
}
}

@ -19,8 +19,11 @@ namespace LightweightIocContainer
/// <summary>
/// One instance is created that gets returned every time an instance is resolved
/// </summary>
Singleton
Singleton,
//TODO: Add Lifestyle.Multiton
/// <summary>
/// A new instance gets created if the given scope has no created instance yet. Otherwise the already created instance is used.
/// </summary>
Multiton
}
}

@ -7,13 +7,12 @@
<Description>A lightweight IOC Container</Description>
<RepositoryUrl>https://github.com/SimonG96/LightweightIocContainer</RepositoryUrl>
<PackageLicenseUrl>https://github.com/SimonG96/LightweightIocContainer/blob/master/LICENSE</PackageLicenseUrl>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<Folder Include="Factories\" />
<Folder Include="Interfaces\Factories\" />
<Folder Include="Interfaces\Registrations\" />
<Folder Include="Registrations\" />
</ItemGroup>
<ItemGroup>

@ -0,0 +1,27 @@
// // Author: Gockner, Simon
// // Created: 2019-06-07
// // Copyright(c) 2019 SimonG. All Rights Reserved.
using System;
using LightweightIocContainer.Interfaces.Registrations;
namespace LightweightIocContainer.Registrations
{
/// <summary>
/// The registration that is used to register a multiton
/// </summary>
/// <typeparam name="TInterface">The registered interface</typeparam>
public class MultitonRegistration<TInterface> : DefaultRegistration<TInterface>, IMultitonRegistration<TInterface>
{
public MultitonRegistration(Type interfaceType, Type implementationType, Type scope)
: base(interfaceType, implementationType, Lifestyle.Multiton)
{
Scope = scope;
}
/// <summary>
/// The type of the multiton scope
/// </summary>
public Type Scope { get; }
}
}

@ -24,6 +24,18 @@ namespace LightweightIocContainer.Registrations
return new DefaultRegistration<TInterface>(typeof(TInterface), typeof(TImplementation), lifestyle);
}
/// <summary>
/// Register an Interface with a Type that implements it as a multiton and create a <see cref="IMultitonRegistration{TInterface}"/>
/// </summary>
/// <typeparam name="TInterface">The Interface to register</typeparam>
/// <typeparam name="TImplementation">The Type that implements the <see cref="TInterface"/></typeparam>
/// <typeparam name="TScope">The Type of the multiton scope</typeparam>
/// <returns>A new created <see cref="IMultitonRegistration{TInterface}"/> with the given parameters</returns>
public static IMultitonRegistration<TInterface> Register<TInterface, TImplementation, TScope>() where TImplementation : TInterface
{
return new MultitonRegistration<TInterface>(typeof(TInterface), typeof(TImplementation), typeof(TScope));
}
/// <summary>
/// Register an Interface as an abstract typed factory and create a <see cref="ITypedFactoryRegistration{TFactory}"/>
/// </summary>

@ -22,6 +22,12 @@ namespace Test.LightweightIocContainer
{
ITest Create();
ITest Create(string name);
ITest Create(MultitonScope scope);
}
private interface ITestFactoryNoCreate
{
}
private class Test : ITest
@ -37,6 +43,11 @@ namespace Test.LightweightIocContainer
}
}
public class MultitonScope
{
}
#endregion
@ -70,12 +81,31 @@ namespace Test.LightweightIocContainer
Assert.AreEqual(iocContainer, returnedContainer);
}
[Test]
public void TestRegisterMultiple()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test>());
var exception = Assert.Throws<MultipleRegistrationException>(() => iocContainer.Register(RegistrationFactory.Register<ITest, TestConstructor>()));
Assert.AreEqual(typeof(ITest), exception.Type);
}
[Test]
public void RegisterFactoryWithoutCreate()
{
IIocContainer iocContainer = new IocContainer();
Assert.Throws<InvalidFactoryRegistrationException>(() => iocContainer.Register(RegistrationFactory.RegisterFactory<ITestFactoryNoCreate>(iocContainer)));
}
[Test]
public void TestResolveNotRegistered()
{
IIocContainer iocContainer = new IocContainer();
Assert.Throws<TypeNotRegisteredException>(() => iocContainer.Resolve<ITest>());
var exception = Assert.Throws<TypeNotRegisteredException>(() => iocContainer.Resolve<ITest>());
Assert.AreEqual(typeof(ITest), exception.Type);
}
[Test]
@ -135,6 +165,44 @@ namespace Test.LightweightIocContainer
Assert.AreEqual(resolvedTest, secondResolvedTest);
}
[Test]
public void TestResolveMultiton()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test, MultitonScope>());
MultitonScope scope1 = new MultitonScope();
MultitonScope scope2 = new MultitonScope();
ITest resolvedTest1 = iocContainer.Resolve<ITest>(scope1);
ITest resolvedTest2 = iocContainer.Resolve<ITest>(scope1);
ITest resolvedTest3 = iocContainer.Resolve<ITest>(scope2);
Assert.AreSame(resolvedTest1, resolvedTest2);
Assert.AreNotSame(resolvedTest1, resolvedTest3);
Assert.AreNotSame(resolvedTest2, resolvedTest3);
}
[Test]
public void TestResolveMultitonNoArgs()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test, MultitonScope>());
var exception = Assert.Throws<MultitonResolveException>(() => iocContainer.Resolve<ITest>());
Assert.AreEqual(typeof(ITest), exception.Type);
}
[Test]
public void TestResolveMultitonWrongArgs()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test, MultitonScope>());
var exception = Assert.Throws<MultitonResolveException>(() => iocContainer.Resolve<ITest>(new object()));
Assert.AreEqual(typeof(ITest), exception.Type);
}
[Test]
public void TestResolveTransient()
{
@ -185,5 +253,26 @@ namespace Test.LightweightIocContainer
Assert.IsInstanceOf<TestConstructor>(createdTest);
}
[Test]
public void TestResolveMultitonFromFactory()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test, MultitonScope>());
iocContainer.Register(RegistrationFactory.RegisterFactory<ITestFactory>(iocContainer));
MultitonScope scope1 = new MultitonScope();
MultitonScope scope2 = new MultitonScope();
ITestFactory testFactory = iocContainer.Resolve<ITestFactory>();
ITest resolvedTest1 = testFactory.Create(scope1);
ITest resolvedTest2 = testFactory.Create(scope1);
ITest resolvedTest3 = testFactory.Create(scope2);
Assert.AreSame(resolvedTest1, resolvedTest2);
Assert.AreNotSame(resolvedTest1, resolvedTest3);
Assert.AreNotSame(resolvedTest2, resolvedTest3);
}
}
}
Loading…
Cancel
Save