Skip to content

Commit 5ea077b

Browse files
Merge pull request #5 from servicetitan/supporting-resolver-overrides
Add supporting of unity resolver overrides.
2 parents b4ab841 + 4e1f53c commit 5ea077b

File tree

3 files changed

+141
-3
lines changed

3 files changed

+141
-3
lines changed

LazyProxy.Unity.Tests/UnityExtensionTests.cs

+65
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
using Unity.Exceptions;
66
using Unity.Injection;
77
using Unity.Lifetime;
8+
using Unity.Resolution;
89
using Xunit;
10+
using DependencyAttribute = Unity.Attributes.DependencyAttribute;
911

1012
[assembly: InternalsVisibleTo("LazyProxy.DynamicTypes")]
1113

@@ -60,6 +62,11 @@ public Service2()
6062
public string Method(string arg) => "service2->" + arg;
6163
}
6264

65+
private class Service2Ex : IService2
66+
{
67+
public string Method(string arg) => "service2Ex->" + arg;
68+
}
69+
6370
internal interface IInternalService
6471
{
6572
string Get();
@@ -126,6 +133,47 @@ public DerivedGenericService(string value)
126133
}
127134
}
128135

136+
public class Argument
137+
{
138+
public string StringValue { get; }
139+
140+
public Argument(string stringValue)
141+
{
142+
StringValue = stringValue;
143+
}
144+
}
145+
146+
enum SomeEnum
147+
{
148+
Value1,
149+
Value2
150+
}
151+
152+
public interface IServiceToTestOverrides
153+
{
154+
string Property { get; set; }
155+
string Get(string arg);
156+
}
157+
158+
private class ServiceToTestOverrides : IServiceToTestOverrides
159+
{
160+
private readonly SomeEnum _someEnum;
161+
private readonly IService2 _service;
162+
private readonly Argument _argument;
163+
164+
[Dependency]
165+
public string Property { get; set; }
166+
167+
public string Get(string arg) => $"{_someEnum}_{_service.Method(arg)}_{Property}_{_argument.StringValue}";
168+
169+
public ServiceToTestOverrides(SomeEnum someEnum, IService2 service, Argument argument)
170+
{
171+
_someEnum = someEnum;
172+
_service = service;
173+
_argument = argument;
174+
}
175+
}
176+
129177
[Fact]
130178
public void ServiceCtorMustBeExecutedAfterMethodIsCalledAndOnlyOnce()
131179
{
@@ -487,5 +535,22 @@ public void GenericServicesMustBeResolvedByNameWithCorrectInjectionMembers()
487535
Assert.Equal($"Argument1A_Argument2_Int32_{value1}", service1.Get(new Argument1A(), new Argument2(), 42).Value);
488536
Assert.Equal($"Argument1A_Argument2_Int32_{value2}", service2.Get(new Argument1A(), new Argument2(), 42).Value);
489537
}
538+
539+
[Fact]
540+
public void OverridesMustBeAppliedByProxy()
541+
{
542+
var result = new UnityContainer()
543+
.RegisterType<IService2, Service2>()
544+
.RegisterLazy<IServiceToTestOverrides, ServiceToTestOverrides>()
545+
.Resolve<IServiceToTestOverrides>(
546+
new ParameterOverride("someEnum", SomeEnum.Value2),
547+
new DependencyOverride(typeof(IService2), new Service2Ex()),
548+
new PropertyOverride("Property", "propertyValue"),
549+
new TypeBasedOverride(typeof(Argument), new ParameterOverride("stringValue", "stringValue"))
550+
)
551+
.Get("arg");
552+
553+
Assert.Equal("Value2_service2Ex->arg_propertyValue_stringValue", result);
554+
}
490555
}
491556
}

LazyProxy.Unity/UnityExtensions.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using Unity;
3-
using Unity.Injection;
43
using Unity.Lifetime;
54
using Unity.Registration;
65

@@ -154,8 +153,8 @@ public static IUnityContainer RegisterLazy(this IUnityContainer container,
154153

155154
return container
156155
.RegisterType(typeFrom, typeTo, registrationName, getLifetimeManager(), injectionMembers)
157-
.RegisterType(typeFrom, name, getLifetimeManager(), new InjectionFactory(
158-
(c, t, n) => LazyProxyBuilder.CreateInstance(t, () => c.Resolve(t, registrationName))));
156+
.RegisterType(typeFrom, name, getLifetimeManager(), new UnityInjectionFactory(
157+
(c, t, n, o) => LazyProxyBuilder.CreateInstance(t, () => c.Resolve(t, registrationName, o))));
159158
}
160159
}
161160
}
+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using Unity;
6+
using Unity.Builder;
7+
using Unity.Injection;
8+
using Unity.Policy;
9+
using Unity.Registration;
10+
using Unity.Resolution;
11+
12+
namespace LazyProxy.Unity
13+
{
14+
/// <summary>
15+
/// A class that lets you specify a factory method the container will use to create the object.
16+
/// </summary>
17+
public class UnityInjectionFactory : InjectionMember, IInjectionFactory, IBuildPlanPolicy
18+
{
19+
private static readonly FieldInfo ResolverOverrides = typeof(BuilderContext)
20+
.GetField("_resolverOverrides", BindingFlags.Instance | BindingFlags.NonPublic);
21+
22+
private readonly Func<IUnityContainer, Type, string, ResolverOverride[], object> _factoryFunc;
23+
24+
/// <summary>
25+
/// Create a new instance of <see cref="UnityInjectionFactory"/> with the given factory function.
26+
/// </summary>
27+
/// <param name="factoryFunc">Factory function.</param>
28+
public UnityInjectionFactory(Func<IUnityContainer, Type, string, ResolverOverride[], object> factoryFunc)
29+
{
30+
_factoryFunc = factoryFunc ?? throw new ArgumentNullException(nameof(factoryFunc));
31+
}
32+
33+
/// <summary>
34+
/// Add policies to the policies to configure the container
35+
/// to call this constructor with the appropriate parameter values.
36+
/// </summary>
37+
/// <param name="serviceType">Type of interface being registered. If no interface, this will be null.
38+
/// This parameter is ignored in this implementation.</param>
39+
/// <param name="implementationType">Type of concrete type being registered.</param>
40+
/// <param name="name">Name used to resolve the type object.</param>
41+
/// <param name="policies">Policy list to add policies to.</param>
42+
public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
43+
{
44+
policies.Set(serviceType, name, typeof(IBuildPlanPolicy), this);
45+
}
46+
47+
/// <summary>
48+
/// Creates an instance of this build plan's type, or fills in the existing type if passed in.
49+
/// </summary>
50+
/// <param name="context">Context used to build up the object.</param>
51+
/// <exception cref="ArgumentNullException">Context is null.</exception>
52+
public void BuildUp(IBuilderContext context)
53+
{
54+
if (context == null)
55+
throw new ArgumentNullException(nameof(context));
56+
57+
if (context.Existing != null)
58+
return;
59+
60+
var resolverOverride = ResolverOverrides.GetValue(context);
61+
62+
var resolverOverrides = resolverOverride == null
63+
? new ResolverOverride[] { }
64+
: ((IEnumerable<ResolverOverride>)resolverOverride).ToArray();
65+
66+
var container = context.Container;
67+
var type = context.BuildKey.Type;
68+
var name = context.BuildKey.Name;
69+
70+
context.Existing = _factoryFunc(container, type, name, resolverOverrides);
71+
context.SetPerBuildSingleton();
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)