diff --git a/DynamicInjector-Test/Program.cs b/DynamicInjector-Test/Program.cs index 8bfa88e..c307a9d 100644 --- a/DynamicInjector-Test/Program.cs +++ b/DynamicInjector-Test/Program.cs @@ -6,14 +6,29 @@ internal class Prgramm { private static void Main() { - InjectionContainer injector = new InjectionContainer(); - injector.RegisterGlobal(); - injector.RegisterLocal(); + InjectionContainer container = new InjectionContainer(); + container.RegisterScoped(); + container.RegisterLocal(); - TestClass? testClass = injector.Resolve(); + using (LifetimeScope scope = container.BeginScope()) + { + TestClass? testClass = scope.Resolve(); + testClass.Test(); + int res = scope.Invoke(testClass, (i) => i.SecondTest); - testClass.Test(); + Console.WriteLine(res); + scope.Invoke(testClass, (i) => i.SecondTest); + scope.Invoke(testClass, (i) => i.SecondTest); + } - injector.Invoke(testClass, (c) => c.Test); + Console.WriteLine(); + + using (LifetimeScope scope = container.BeginScope()) + { + scope.Resolve().Test(); + scope.Resolve().Test(); + scope.Resolve().Test(); + scope.Resolve().Test(); + } } } \ No newline at end of file diff --git a/DynamicInjector-Test/TestClass.cs b/DynamicInjector-Test/TestClass.cs index dfc9de7..4534bbd 100644 --- a/DynamicInjector-Test/TestClass.cs +++ b/DynamicInjector-Test/TestClass.cs @@ -16,7 +16,6 @@ internal class TestClass public void Test() { - testService.Print(); SecondTestService?.Output(); } diff --git a/DynamicInjector/IContainer.cs b/DynamicInjector/IContainer.cs new file mode 100644 index 0000000..4653a2c --- /dev/null +++ b/DynamicInjector/IContainer.cs @@ -0,0 +1,13 @@ +using System; + +namespace DynamicInjector +{ + public interface IContainer + { + public object Invoke(TInstance instance, Func method); + + public object Resolve(Type type); + + public T Resolve(); + } +} \ No newline at end of file diff --git a/DynamicInjector/InjectionContainer.cs b/DynamicInjector/InjectionContainer.cs index 22ca788..7ca5543 100644 --- a/DynamicInjector/InjectionContainer.cs +++ b/DynamicInjector/InjectionContainer.cs @@ -4,10 +4,11 @@ using System.Reflection; namespace DynamicInjector { - public class InjectionContainer + public class InjectionContainer : IContainer { private readonly Dictionary globalServices = new Dictionary(); private readonly Dictionary localServices = new Dictionary(); + private readonly Dictionary scopedServices = new Dictionary(); public void RegisterGlobal(string? name = null) { @@ -36,7 +37,22 @@ namespace DynamicInjector public void RegisterLocal(string? name = null) where TImplementation : TService { - localServices.Add(name ?? typeof(TService).FullName, typeof(TService)); + localServices.Add(name ?? typeof(TService).FullName, typeof(TImplementation)); + } + + public void RegisterScoped(string? name = null) + { + scopedServices.Add(name ?? typeof(TService).FullName, typeof(TService)); + } + + public void RegisterScoped(string? name = null) where TImplementation : TService + { + scopedServices.Add(name ?? typeof(TService).FullName, typeof(TImplementation)); + } + + public LifetimeScope BeginScope() + { + return new LifetimeScope(this); } public T Resolve() @@ -45,6 +61,11 @@ namespace DynamicInjector } public object Resolve(Type type) + { + return InternalResolve(type, null); + } + + internal object InternalResolve(Type type, LifetimeScope? scope) { ConstructorInfo[]? constructorInfos = type.GetConstructors(); @@ -71,11 +92,11 @@ namespace DynamicInjector foreach (ParameterInfo parameterInfo in parameterInfos) { - if (CheckIfServiceExists(parameterInfo.Name, out object? serviceInstance)) + if (CheckIfServiceExists(parameterInfo.Name, scope, out object? serviceInstance)) { arguments.Add(serviceInstance!); } - else if (CheckIfServiceExists(parameterInfo.ParameterType.FullName, out serviceInstance)) + else if (CheckIfServiceExists(parameterInfo.ParameterType.FullName, scope, out serviceInstance)) { arguments.Add(serviceInstance!); } @@ -94,11 +115,11 @@ namespace DynamicInjector continue; } - if (CheckIfServiceExists(propertyInfo.Name, out object? serviceInstance)) + if (CheckIfServiceExists(propertyInfo.Name, scope, out object? serviceInstance)) { propertyInfo.SetValue(instance, serviceInstance); } - else if (CheckIfServiceExists(propertyInfo.PropertyType.FullName, out serviceInstance)) + else if (CheckIfServiceExists(propertyInfo.PropertyType.FullName, scope, out serviceInstance)) { propertyInfo.SetValue(instance, serviceInstance); } @@ -112,6 +133,11 @@ namespace DynamicInjector } public object Invoke(TInstance instance, Func method) + { + return InternalInvoke(instance, method, null); + } + + internal object InternalInvoke(TInstance instance, Func method, LifetimeScope? scope) { Delegate delegateMethod = method.Invoke(instance); MethodInfo methodInfo = delegateMethod.Method; @@ -126,11 +152,11 @@ namespace DynamicInjector foreach (ParameterInfo parameterInfo in parameterInfos) { - if (CheckIfServiceExists(parameterInfo.Name, out object? serviceInstance)) + if (CheckIfServiceExists(parameterInfo.Name, scope, out object? serviceInstance)) { arguments.Add(serviceInstance!); } - else if (CheckIfServiceExists(parameterInfo.ParameterType.FullName, out serviceInstance)) + else if (CheckIfServiceExists(parameterInfo.ParameterType.FullName, scope, out serviceInstance)) { arguments.Add(serviceInstance!); } @@ -143,8 +169,17 @@ namespace DynamicInjector return delegateMethod.DynamicInvoke(arguments.ToArray()); } - private bool CheckIfServiceExists(string typeName, out object? serviceInstance) + private bool CheckIfServiceExists(string typeName, LifetimeScope? scope, out object? serviceInstance) { + foreach (KeyValuePair serviceType in localServices) + { + if (typeName == serviceType.Key) + { + serviceInstance = InternalResolve(localServices[serviceType.Key], scope); + return true; + } + } + foreach (KeyValuePair serviceType in globalServices) { if (typeName == serviceType.Key) @@ -154,11 +189,25 @@ namespace DynamicInjector } } - foreach (KeyValuePair serviceType in localServices) + if (scope is null) + { + serviceInstance = null; + return false; + } + + foreach (KeyValuePair serviceType in scopedServices) { if (typeName == serviceType.Key) { - serviceInstance = Resolve(localServices[serviceType.Key]); + if (scope.Services.ContainsKey(serviceType.Key)) + { + serviceInstance = scope.Services[serviceType.Key]; + } + else + { + serviceInstance = InternalResolve(scopedServices[serviceType.Key], scope); + scope.Services.Add(serviceType.Key, serviceInstance); + } return true; } } diff --git a/DynamicInjector/LifetimeScope.cs b/DynamicInjector/LifetimeScope.cs new file mode 100644 index 0000000..18fce41 --- /dev/null +++ b/DynamicInjector/LifetimeScope.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; + +namespace DynamicInjector +{ + public class LifetimeScope : IContainer, IDisposable + { + private readonly InjectionContainer injectionContainer; + + public Dictionary Services { get; } = new Dictionary(); + + public LifetimeScope(InjectionContainer injectionContainer) + { + this.injectionContainer = injectionContainer; + } + + public T Resolve() + { + return (T)Resolve(typeof(T)); + } + + public object Resolve(Type type) + { + return injectionContainer.InternalResolve(type, this); + } + + public object Invoke(TInstance instance, Func method) + { + return injectionContainer.InternalInvoke(instance, method, this); + } + + public void Dispose() + { + foreach (object? service in Services.Values) + { + if (service is IDisposable disposableService) + { + disposableService.Dispose(); + } + } + } + } +} \ No newline at end of file diff --git a/DynamicInjector/MethodInjectionContainerExtensions.cs b/DynamicInjector/MethodInjectionContainerExtensions.cs index 2724525..0bb23de 100644 --- a/DynamicInjector/MethodInjectionContainerExtensions.cs +++ b/DynamicInjector/MethodInjectionContainerExtensions.cs @@ -4,79 +4,79 @@ namespace DynamicInjector { public static class MethodInjectionContainerExtensions { - public static void Invoke(this InjectionContainer injectionContainer, Func method) + public static void Invoke(this IContainer injectionContainer, Func method) { - injectionContainer.Invoke(injectionContainer.Resolve(), method); + injectionContainer.Invoke(injectionContainer.Resolve(), method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, TInstance instance, Func> method) + public static TResult Invoke(this IContainer injectionContainer, T instance, Func> method) { - return (TReturn)injectionContainer.Invoke(instance, method); + return (TResult)injectionContainer.Invoke(instance, method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, TInstance instance, Func> method) + public static TResult Invoke(this IContainer injectionContainer, T instance, Func> method) { - return (TReturn)injectionContainer.Invoke(instance, method); + return (TResult)injectionContainer.Invoke(instance, method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, TInstance instance, Func> method) + public static TResult Invoke(this IContainer injectionContainer, T instance, Func> method) { - return (TReturn)injectionContainer.Invoke(instance, method); + return (TResult)injectionContainer.Invoke(instance, method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, TInstance instance, Func> method) + public static TResult Invoke(this IContainer injectionContainer, T instance, Func> method) { - return (TReturn)injectionContainer.Invoke(instance, method); + return (TResult)injectionContainer.Invoke(instance, method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, TInstance instance, Func> method) + public static TResult Invoke(this IContainer injectionContainer, T instance, Func> method) { - return (TReturn)injectionContainer.Invoke(instance, method); + return (TResult)injectionContainer.Invoke(instance, method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, TInstance instance, Func> method) + public static TResult Invoke(this IContainer injectionContainer, T instance, Func> method) { - return (TReturn)injectionContainer.Invoke(instance, method); + return (TResult)injectionContainer.Invoke(instance, method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, TInstance instance, Func> method) + public static TResult Invoke(this IContainer injectionContainer, T instance, Func> method) { - return (TReturn)injectionContainer.Invoke(instance, method); + return (TResult)injectionContainer.Invoke(instance, method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, Func> method) + public static TResult Invoke(this IContainer injectionContainer, Func> method) { - return (TReturn)injectionContainer.Invoke(injectionContainer.Resolve(), method); + return (TResult)injectionContainer.Invoke(injectionContainer.Resolve(), method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, Func> method) + public static TResult Invoke(this IContainer injectionContainer, Func> method) { - return (TReturn)injectionContainer.Invoke(injectionContainer.Resolve(), method); + return (TResult)injectionContainer.Invoke(injectionContainer.Resolve(), method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, Func> method) + public static TResult Invoke(this IContainer injectionContainer, Func> method) { - return (TReturn)injectionContainer.Invoke(injectionContainer.Resolve(), method); + return (TResult)injectionContainer.Invoke(injectionContainer.Resolve(), method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, Func> method) + public static TResult Invoke(this IContainer injectionContainer, Func> method) { - return (TReturn)injectionContainer.Invoke(injectionContainer.Resolve(), method); + return (TResult)injectionContainer.Invoke(injectionContainer.Resolve(), method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, Func> method) + public static TResult Invoke(this IContainer injectionContainer, Func> method) { - return (TReturn)injectionContainer.Invoke(injectionContainer.Resolve(), method); + return (TResult)injectionContainer.Invoke(injectionContainer.Resolve(), method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, Func> method) + public static TResult Invoke(this IContainer injectionContainer, Func> method) { - return (TReturn)injectionContainer.Invoke(injectionContainer.Resolve(), method); + return (TResult)injectionContainer.Invoke(injectionContainer.Resolve(), method); } - public static TReturn Invoke(this InjectionContainer injectionContainer, Func> method) + public static TResult Invoke(this IContainer injectionContainer, Func> method) { - return (TReturn)injectionContainer.Invoke(injectionContainer.Resolve(), method); + return (TResult)injectionContainer.Invoke(injectionContainer.Resolve(), method); } } } \ No newline at end of file