diff --git a/LazyProxy.Unity.Tests/UnityExtensionTests.cs b/LazyProxy.Unity.Tests/UnityExtensionTests.cs index 303451a..a042a63 100644 --- a/LazyProxy.Unity.Tests/UnityExtensionTests.cs +++ b/LazyProxy.Unity.Tests/UnityExtensionTests.cs @@ -5,7 +5,9 @@ using Unity.Exceptions; using Unity.Injection; using Unity.Lifetime; +using Unity.Resolution; using Xunit; +using DependencyAttribute = Unity.Attributes.DependencyAttribute; [assembly: InternalsVisibleTo("LazyProxy.DynamicTypes")] @@ -60,6 +62,11 @@ public Service2() public string Method(string arg) => "service2->" + arg; } + private class Service2Ex : IService2 + { + public string Method(string arg) => "service2Ex->" + arg; + } + internal interface IInternalService { string Get(); @@ -126,6 +133,47 @@ public DerivedGenericService(string value) } } + public class Argument + { + public string StringValue { get; } + + public Argument(string stringValue) + { + StringValue = stringValue; + } + } + + enum SomeEnum + { + Value1, + Value2 + } + + public interface IServiceToTestOverrides + { + string Property { get; set; } + string Get(string arg); + } + + private class ServiceToTestOverrides : IServiceToTestOverrides + { + private readonly SomeEnum _someEnum; + private readonly IService2 _service; + private readonly Argument _argument; + + [Dependency] + public string Property { get; set; } + + public string Get(string arg) => $"{_someEnum}_{_service.Method(arg)}_{Property}_{_argument.StringValue}"; + + public ServiceToTestOverrides(SomeEnum someEnum, IService2 service, Argument argument) + { + _someEnum = someEnum; + _service = service; + _argument = argument; + } + } + [Fact] public void ServiceCtorMustBeExecutedAfterMethodIsCalledAndOnlyOnce() { @@ -487,5 +535,22 @@ public void GenericServicesMustBeResolvedByNameWithCorrectInjectionMembers() 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); } + + [Fact] + public void OverridesMustBeAppliedByProxy() + { + var result = new UnityContainer() + .RegisterType() + .RegisterLazy() + .Resolve( + new ParameterOverride("someEnum", SomeEnum.Value2), + new DependencyOverride(typeof(IService2), new Service2Ex()), + new PropertyOverride("Property", "propertyValue"), + new TypeBasedOverride(typeof(Argument), new ParameterOverride("stringValue", "stringValue")) + ) + .Get("arg"); + + Assert.Equal("Value2_service2Ex->arg_propertyValue_stringValue", result); + } } } \ No newline at end of file diff --git a/LazyProxy.Unity/UnityExtensions.cs b/LazyProxy.Unity/UnityExtensions.cs index b8ff0cc..d79ad12 100644 --- a/LazyProxy.Unity/UnityExtensions.cs +++ b/LazyProxy.Unity/UnityExtensions.cs @@ -1,6 +1,5 @@ using System; using Unity; -using Unity.Injection; using Unity.Lifetime; using Unity.Registration; @@ -154,8 +153,8 @@ public static IUnityContainer RegisterLazy(this IUnityContainer container, return container .RegisterType(typeFrom, typeTo, registrationName, getLifetimeManager(), injectionMembers) - .RegisterType(typeFrom, name, getLifetimeManager(), new InjectionFactory( - (c, t, n) => LazyProxyBuilder.CreateInstance(t, () => c.Resolve(t, registrationName)))); + .RegisterType(typeFrom, name, getLifetimeManager(), new UnityInjectionFactory( + (c, t, n, o) => LazyProxyBuilder.CreateInstance(t, () => c.Resolve(t, registrationName, o)))); } } } \ No newline at end of file diff --git a/LazyProxy.Unity/UnityInjectionFactory.cs b/LazyProxy.Unity/UnityInjectionFactory.cs new file mode 100644 index 0000000..e6f6906 --- /dev/null +++ b/LazyProxy.Unity/UnityInjectionFactory.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Unity; +using Unity.Builder; +using Unity.Injection; +using Unity.Policy; +using Unity.Registration; +using Unity.Resolution; + +namespace LazyProxy.Unity +{ + /// + /// A class that lets you specify a factory method the container will use to create the object. + /// + public class UnityInjectionFactory : InjectionMember, IInjectionFactory, IBuildPlanPolicy + { + private static readonly FieldInfo ResolverOverrides = typeof(BuilderContext) + .GetField("_resolverOverrides", BindingFlags.Instance | BindingFlags.NonPublic); + + private readonly Func _factoryFunc; + + /// + /// Create a new instance of with the given factory function. + /// + /// Factory function. + public UnityInjectionFactory(Func factoryFunc) + { + _factoryFunc = factoryFunc ?? throw new ArgumentNullException(nameof(factoryFunc)); + } + + /// + /// Add policies to the policies to configure the container + /// to call this constructor with the appropriate parameter values. + /// + /// Type of interface being registered. If no interface, this will be null. + /// This parameter is ignored in this implementation. + /// Type of concrete type being registered. + /// Name used to resolve the type object. + /// Policy list to add policies to. + public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies) + { + policies.Set(serviceType, name, typeof(IBuildPlanPolicy), this); + } + + /// + /// Creates an instance of this build plan's type, or fills in the existing type if passed in. + /// + /// Context used to build up the object. + /// Context is null. + public void BuildUp(IBuilderContext context) + { + if (context == null) + throw new ArgumentNullException(nameof(context)); + + if (context.Existing != null) + return; + + var resolverOverride = ResolverOverrides.GetValue(context); + + var resolverOverrides = resolverOverride == null + ? new ResolverOverride[] { } + : ((IEnumerable)resolverOverride).ToArray(); + + var container = context.Container; + var type = context.BuildKey.Type; + var name = context.BuildKey.Name; + + context.Existing = _factoryFunc(container, type, name, resolverOverrides); + context.SetPerBuildSingleton(); + } + } +} \ No newline at end of file