diff --git a/LazyProxy.Unity.Tests/UnityExtensionTests.cs b/LazyProxy.Unity.Tests/UnityExtensionTests.cs index 4f68820..303451a 100644 --- a/LazyProxy.Unity.Tests/UnityExtensionTests.cs +++ b/LazyProxy.Unity.Tests/UnityExtensionTests.cs @@ -70,6 +70,62 @@ internal class InternalService : IInternalService public string Get() => "InternalService"; } + public interface IBaseArgument { } + + public abstract class AnotherArgument + { + public string Value { get; set; } + } + + public class Argument1A : IBaseArgument { } + + public class Argument1B : IBaseArgument { } + + public class Argument1C : IBaseArgument { } + + public struct Argument2 { } + + public class Argument3 : AnotherArgument, IBaseArgument { } + + public interface IGenericService + where T : class, IBaseArgument, new() + where TIn : struct + where TOut : AnotherArgument, IBaseArgument + { + TOut Get(T arg1, TIn arg2, TArg arg3) where TArg : struct; + } + + private class GenericService : IGenericService + where T : class, IBaseArgument, new() + where TIn : struct + where TOut : AnotherArgument, IBaseArgument, new() + { + protected virtual string Get() => ""; + + public TOut Get(T arg1, TIn arg2, TArg arg3) where TArg : struct + { + return new TOut + { + Value = $"{arg1.GetType().Name}_{arg2.GetType().Name}_{arg3.GetType().Name}{Get()}" + }; + } + } + + private class DerivedGenericService : GenericService + where T : class, IBaseArgument, new() + where TIn : struct + where TOut : AnotherArgument, IBaseArgument, new() + { + private readonly string _value; + + protected override string Get() => $"_{_value}"; + + public DerivedGenericService(string value) + { + _value = value; + } + } + [Fact] public void ServiceCtorMustBeExecutedAfterMethodIsCalledAndOnlyOnce() { @@ -305,5 +361,131 @@ public void InternalsVisibleToAttributeMustAllowToResolveInternalServices() Assert.Equal("InternalService", result); } + + [Fact] + public void ClosedGenericServiceMustBeResolved() + { + var result = new UnityContainer() + .RegisterLazy( + typeof(IGenericService), + typeof(GenericService)) + .Resolve>() + .Get(new Argument1A(), new Argument2(), 42); + + Assert.Equal("Argument1A_Argument2_Int32", result.Value); + } + + [Fact] + public void OpenedGenericServiceMustBeResolved() + { + var result = new UnityContainer() + .RegisterLazy(typeof(IGenericService<,,>), typeof(GenericService<,,>)) + .Resolve>() + .Get(new Argument1A(), new Argument2(), 42); + + Assert.Equal("Argument1A_Argument2_Int32", result.Value); + } + + [Fact] + public void GenericServiceMustBeResolvedWithCorrectLifetime() + { + var container = new UnityContainer() + .RegisterLazy( + typeof(IGenericService<,,>), + typeof(GenericService<,,>), + () => new TransientLifetimeManager()) + .RegisterLazy( + typeof(IGenericService), + typeof(GenericService), + () => new ContainerControlledLifetimeManager()) + .RegisterLazy( + typeof(IGenericService), + typeof(GenericService), + () => new HierarchicalLifetimeManager()) + .RegisterLazy( + typeof(IGenericService), + typeof(GenericService), + () => new TransientLifetimeManager()); + + var service1 = container.Resolve>(); + var service2 = container.Resolve>(); + var service3 = container.Resolve>(); + + Assert.Same(service1, container.Resolve>()); + Assert.Same(service2, container.Resolve>()); + Assert.NotSame(service3, container.Resolve>()); + + using (var childContainer = container.CreateChildContainer()) + { + Assert.Same(service1, childContainer.Resolve>()); + Assert.NotSame(service2, childContainer.Resolve>()); + Assert.NotSame(service3, childContainer.Resolve>()); + } + } + + [Fact] + public void GenericServicesMustBeResolvedByNameWithCorrectLifetime() + { + const string singleton = "singleton"; + const string hierarchical = "hierarchical"; + const string transient = "transient"; + + var container = new UnityContainer() + .RegisterLazy( + typeof(IGenericService<,,>), + typeof(GenericService<,,>), + singleton, + () => new ContainerControlledLifetimeManager()) + .RegisterLazy( + typeof(IGenericService<,,>), + typeof(GenericService<,,>), + hierarchical, + () => new HierarchicalLifetimeManager()) + .RegisterLazy( + typeof(IGenericService<,,>), + typeof(GenericService<,,>), + transient, + () => new TransientLifetimeManager()); + + var service1 = container.Resolve>(singleton); + var service2 = container.Resolve>(hierarchical); + var service3 = container.Resolve>(transient); + + Assert.Same(service1, container.Resolve>(singleton)); + Assert.Same(service2, container.Resolve>(hierarchical)); + Assert.NotSame(service3, container.Resolve>(transient)); + + using (var childContainer = container.CreateChildContainer()) + { + Assert.Same(service1, childContainer.Resolve>(singleton)); + Assert.NotSame(service2, childContainer.Resolve>(hierarchical)); + Assert.NotSame(service3, childContainer.Resolve>(transient)); + } + } + + [Fact] + public void GenericServicesMustBeResolvedByNameWithCorrectInjectionMembers() + { + const string value1 = "value1"; + const string value2 = "value2"; + + var container = new UnityContainer() + .RegisterLazy( + typeof(IGenericService<,,>), + typeof(DerivedGenericService<,,>), + value1, + new InjectionConstructor(value1)) + .RegisterLazy( + typeof(IGenericService<,,>), + typeof(DerivedGenericService<,,>), + value2, + new InjectionConstructor(value2)); + + var service1 = container.Resolve>(value1); + var service2 = container.Resolve>(value2); + + Assert.Equal($"Argument1A_Argument2_Int32_{value1}", service1.Get(new Argument1A(), new Argument2(), 42).Value); + Assert.Equal($"Argument1A_Argument2_Int32_{value2}", service2.Get(new Argument1A(), new Argument2(), 42).Value); + } } } \ No newline at end of file diff --git a/LazyProxy.Unity/LazyProxy.Unity.csproj b/LazyProxy.Unity/LazyProxy.Unity.csproj index 9e8e48d..381842a 100644 --- a/LazyProxy.Unity/LazyProxy.Unity.csproj +++ b/LazyProxy.Unity/LazyProxy.Unity.csproj @@ -15,7 +15,7 @@ true - + \ No newline at end of file diff --git a/LazyProxy.Unity/UnityExtensions.cs b/LazyProxy.Unity/UnityExtensions.cs index ef10f3c..b8ff0cc 100644 --- a/LazyProxy.Unity/UnityExtensions.cs +++ b/LazyProxy.Unity/UnityExtensions.cs @@ -6,6 +6,9 @@ namespace LazyProxy.Unity { + /// + /// Extension methods for lazy registration. + /// public static class UnityExtensions { private static readonly Func GetTransientLifetimeManager = () => new TransientLifetimeManager(); @@ -147,16 +150,12 @@ public static IUnityContainer RegisterLazy(this IUnityContainer container, throw new NotSupportedException("The lazy registration is supported only for interfaces."); } - var lazyProxyType = LazyProxyBuilder.BuildLazyProxyType(typeFrom); var registrationName = Guid.NewGuid().ToString(); return container .RegisterType(typeFrom, typeTo, registrationName, getLifetimeManager(), injectionMembers) - .RegisterType(typeFrom, lazyProxyType, name, - getLifetimeManager(), - new InjectionConstructor( - new ResolvedParameter(typeof(Lazy<>).MakeGenericType(typeFrom), registrationName)) - ); + .RegisterType(typeFrom, name, getLifetimeManager(), new InjectionFactory( + (c, t, n) => LazyProxyBuilder.CreateInstance(t, () => c.Resolve(t, registrationName)))); } } } \ No newline at end of file