Skip to content

Commit

Permalink
Add supporting of unity resolver overrides.
Browse files Browse the repository at this point in the history
  • Loading branch information
hypercodeplace committed Nov 30, 2018
1 parent b4ab841 commit 4e1f53c
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 3 deletions.
65 changes: 65 additions & 0 deletions LazyProxy.Unity.Tests/UnityExtensionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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<IService2, Service2>()
.RegisterLazy<IServiceToTestOverrides, ServiceToTestOverrides>()
.Resolve<IServiceToTestOverrides>(
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);
}
}
}
5 changes: 2 additions & 3 deletions LazyProxy.Unity/UnityExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using Unity;
using Unity.Injection;
using Unity.Lifetime;
using Unity.Registration;

Expand Down Expand Up @@ -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))));
}
}
}
74 changes: 74 additions & 0 deletions LazyProxy.Unity/UnityInjectionFactory.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// A class that lets you specify a factory method the container will use to create the object.
/// </summary>
public class UnityInjectionFactory : InjectionMember, IInjectionFactory, IBuildPlanPolicy
{
private static readonly FieldInfo ResolverOverrides = typeof(BuilderContext)
.GetField("_resolverOverrides", BindingFlags.Instance | BindingFlags.NonPublic);

private readonly Func<IUnityContainer, Type, string, ResolverOverride[], object> _factoryFunc;

/// <summary>
/// Create a new instance of <see cref="UnityInjectionFactory"/> with the given factory function.
/// </summary>
/// <param name="factoryFunc">Factory function.</param>
public UnityInjectionFactory(Func<IUnityContainer, Type, string, ResolverOverride[], object> factoryFunc)
{
_factoryFunc = factoryFunc ?? throw new ArgumentNullException(nameof(factoryFunc));
}

/// <summary>
/// Add policies to the policies to configure the container
/// to call this constructor with the appropriate parameter values.
/// </summary>
/// <param name="serviceType">Type of interface being registered. If no interface, this will be null.
/// This parameter is ignored in this implementation.</param>
/// <param name="implementationType">Type of concrete type being registered.</param>
/// <param name="name">Name used to resolve the type object.</param>
/// <param name="policies">Policy list to add policies to.</param>
public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
{
policies.Set(serviceType, name, typeof(IBuildPlanPolicy), this);
}

/// <summary>
/// Creates an instance of this build plan's type, or fills in the existing type if passed in.
/// </summary>
/// <param name="context">Context used to build up the object.</param>
/// <exception cref="ArgumentNullException">Context is null.</exception>
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>)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();
}
}
}

0 comments on commit 4e1f53c

Please sign in to comment.