diff --git a/LightweightIocContainer.sln.DotSettings b/LightweightIocContainer.sln.DotSettings
index d75d405..3278b56 100644
--- a/LightweightIocContainer.sln.DotSettings
+++ b/LightweightIocContainer.sln.DotSettings
@@ -2,4 +2,6 @@
// Author: $USER_NAME$
// Created: $CREATED_YEAR$-$CREATED_MONTH$-$CREATED_DAY$
// Copyright(c) $CREATED_YEAR$ SimonG. All Rights Reserved.
- <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
\ No newline at end of file
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ True
+ True
\ No newline at end of file
diff --git a/LightweightIocContainer/Exceptions/MultitonResolveException.cs b/LightweightIocContainer/Exceptions/MultitonResolveException.cs
new file mode 100644
index 0000000..057693f
--- /dev/null
+++ b/LightweightIocContainer/Exceptions/MultitonResolveException.cs
@@ -0,0 +1,25 @@
+// // Author: Gockner, Simon
+// // Created: 2019-06-07
+// // Copyright(c) 2019 SimonG. All Rights Reserved.
+
+using System;
+
+namespace LightweightIocContainer.Exceptions
+{
+ ///
+ /// An error happened while trying to resolve a multiton
+ ///
+ public class MultitonResolveException : InternalResolveException
+ {
+ public MultitonResolveException(string message, Type type)
+ : base(message)
+ {
+ Type = type;
+ }
+
+ ///
+ /// The type of the multiton that's responsible for the exception
+ ///
+ public Type Type { get; }
+ }
+}
\ No newline at end of file
diff --git a/LightweightIocContainer/Interfaces/Registrations/IMultitonRegistration.cs b/LightweightIocContainer/Interfaces/Registrations/IMultitonRegistration.cs
new file mode 100644
index 0000000..54927e9
--- /dev/null
+++ b/LightweightIocContainer/Interfaces/Registrations/IMultitonRegistration.cs
@@ -0,0 +1,20 @@
+// // Author: Gockner, Simon
+// // Created: 2019-06-07
+// // Copyright(c) 2019 SimonG. All Rights Reserved.
+
+using System;
+
+namespace LightweightIocContainer.Interfaces.Registrations
+{
+ ///
+ /// The registration that is used to register a multiton
+ ///
+ /// The registered interface
+ public interface IMultitonRegistration : IDefaultRegistration
+ {
+ ///
+ /// The type of the multiton scope
+ ///
+ Type Scope { get; }
+ }
+}
\ No newline at end of file
diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs
index e1378f2..9766856 100644
--- a/LightweightIocContainer/IocContainer.cs
+++ b/LightweightIocContainer/IocContainer.cs
@@ -19,6 +19,7 @@ namespace LightweightIocContainer
{
private readonly List _registrations = new List();
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)>)>();
///
/// Install the given installers for the current
@@ -106,6 +107,8 @@ namespace LightweightIocContainer
{
if (defaultRegistration.Lifestyle == Lifestyle.Singleton)
return GetOrCreateSingletonInstance(defaultRegistration, arguments);
+ else if (defaultRegistration is IMultitonRegistration multitonRegistration && defaultRegistration.Lifestyle == Lifestyle.Multiton)
+ return GetOrCreateMultitonInstance(multitonRegistration, arguments);
return CreateInstance(defaultRegistration, arguments);
}
@@ -122,7 +125,7 @@ namespace LightweightIocContainer
///
/// The given type
/// The registration of the given type
- /// The constructor arguments
+ /// The arguments to resolve
/// An existing or newly created singleton instance of the given type
private T GetOrCreateSingletonInstance(IDefaultRegistration registration, params object[] arguments)
{
@@ -138,6 +141,44 @@ namespace LightweightIocContainer
return newInstance;
}
+ ///
+ /// Gets or creates a multiton instance of a given type
+ ///
+ /// The given type
+ /// The registration of the given type
+ /// The arguments to resolve
+ /// An existing or newly created multiton instance of the given type
+ /// No arguments given
+ /// Scope argument not given
+ private T GetOrCreateMultitonInstance(IMultitonRegistration 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(registration, arguments);
+ instances.Add((scopeArgument, createdInstance));
+
+ return createdInstance;
+ }
+
+ T newInstance = CreateInstance(registration, arguments);
+ _multitons.Add((typeof(T), registration.Scope, new List<(object, object)>() {(scopeArgument, newInstance)}));
+
+ return newInstance;
+ }
+
///
/// Creates an instance of a given type
///
@@ -206,6 +247,7 @@ namespace LightweightIocContainer
{
_registrations.Clear();
_singletons.Clear();
+ _multitons.Clear();
}
}
}
\ No newline at end of file
diff --git a/LightweightIocContainer/Lifestyle.cs b/LightweightIocContainer/Lifestyle.cs
index 4582a92..d3dc7fc 100644
--- a/LightweightIocContainer/Lifestyle.cs
+++ b/LightweightIocContainer/Lifestyle.cs
@@ -19,8 +19,11 @@ namespace LightweightIocContainer
///
/// One instance is created that gets returned every time an instance is resolved
///
- Singleton
+ Singleton,
- //TODO: Add Lifestyle.Multiton
+ ///
+ /// A new instance gets created if the given scope has no created instance yet. Otherwise the already created instance is used.
+ ///
+ Multiton
}
}
\ No newline at end of file
diff --git a/LightweightIocContainer/LightweightIocContainer.csproj b/LightweightIocContainer/LightweightIocContainer.csproj
index b49c198..e4d8381 100644
--- a/LightweightIocContainer/LightweightIocContainer.csproj
+++ b/LightweightIocContainer/LightweightIocContainer.csproj
@@ -7,13 +7,12 @@
A lightweight IOC Container
https://github.com/SimonG96/LightweightIocContainer
https://github.com/SimonG96/LightweightIocContainer/blob/master/LICENSE
+ latest
-
-
diff --git a/LightweightIocContainer/Registrations/MultitonRegistration.cs b/LightweightIocContainer/Registrations/MultitonRegistration.cs
new file mode 100644
index 0000000..92e8694
--- /dev/null
+++ b/LightweightIocContainer/Registrations/MultitonRegistration.cs
@@ -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
+{
+ ///
+ /// The registration that is used to register a multiton
+ ///
+ /// The registered interface
+ public class MultitonRegistration : DefaultRegistration, IMultitonRegistration
+ {
+ public MultitonRegistration(Type interfaceType, Type implementationType, Type scope)
+ : base(interfaceType, implementationType, Lifestyle.Multiton)
+ {
+ Scope = scope;
+ }
+
+ ///
+ /// The type of the multiton scope
+ ///
+ public Type Scope { get; }
+ }
+}
\ No newline at end of file
diff --git a/LightweightIocContainer/Registrations/RegistrationFactory.cs b/LightweightIocContainer/Registrations/RegistrationFactory.cs
index 1bb72a2..eefd5fd 100644
--- a/LightweightIocContainer/Registrations/RegistrationFactory.cs
+++ b/LightweightIocContainer/Registrations/RegistrationFactory.cs
@@ -24,6 +24,18 @@ namespace LightweightIocContainer.Registrations
return new DefaultRegistration(typeof(TInterface), typeof(TImplementation), lifestyle);
}
+ ///
+ /// Register an Interface with a Type that implements it as a multiton and create a
+ ///
+ /// The Interface to register
+ /// The Type that implements the
+ /// The Type of the multiton scope
+ /// A new created with the given parameters
+ public static IMultitonRegistration Register() where TImplementation : TInterface
+ {
+ return new MultitonRegistration(typeof(TInterface), typeof(TImplementation), typeof(TScope));
+ }
+
///
/// Register an Interface as an abstract typed factory and create a
///
diff --git a/Test.LightweightIocContainer/IocContainerTest.cs b/Test.LightweightIocContainer/IocContainerTest.cs
index 3a08a71..7a6e99b 100644
--- a/Test.LightweightIocContainer/IocContainerTest.cs
+++ b/Test.LightweightIocContainer/IocContainerTest.cs
@@ -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());
+ var exception = Assert.Throws(() => iocContainer.Register(RegistrationFactory.Register()));
+ Assert.AreEqual(typeof(ITest), exception.Type);
+ }
+
+ [Test]
+ public void RegisterFactoryWithoutCreate()
+ {
+ IIocContainer iocContainer = new IocContainer();
+
+ Assert.Throws(() => iocContainer.Register(RegistrationFactory.RegisterFactory(iocContainer)));
+ }
+
[Test]
public void TestResolveNotRegistered()
{
IIocContainer iocContainer = new IocContainer();
- Assert.Throws(() => iocContainer.Resolve());
+ var exception = Assert.Throws(() => iocContainer.Resolve());
+ 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());
+
+ MultitonScope scope1 = new MultitonScope();
+ MultitonScope scope2 = new MultitonScope();
+
+ ITest resolvedTest1 = iocContainer.Resolve(scope1);
+ ITest resolvedTest2 = iocContainer.Resolve(scope1);
+ ITest resolvedTest3 = iocContainer.Resolve(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());
+
+ var exception = Assert.Throws(() => iocContainer.Resolve());
+ Assert.AreEqual(typeof(ITest), exception.Type);
+ }
+
+ [Test]
+ public void TestResolveMultitonWrongArgs()
+ {
+ IIocContainer iocContainer = new IocContainer();
+ iocContainer.Register(RegistrationFactory.Register());
+
+ var exception = Assert.Throws(() => iocContainer.Resolve(new object()));
+ Assert.AreEqual(typeof(ITest), exception.Type);
+ }
+
[Test]
public void TestResolveTransient()
{
@@ -185,5 +253,26 @@ namespace Test.LightweightIocContainer
Assert.IsInstanceOf(createdTest);
}
+
+ [Test]
+ public void TestResolveMultitonFromFactory()
+ {
+ IIocContainer iocContainer = new IocContainer();
+ iocContainer.Register(RegistrationFactory.Register());
+ iocContainer.Register(RegistrationFactory.RegisterFactory(iocContainer));
+
+ MultitonScope scope1 = new MultitonScope();
+ MultitonScope scope2 = new MultitonScope();
+
+ ITestFactory testFactory = iocContainer.Resolve();
+
+ 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);
+ }
}
}
\ No newline at end of file