Skip to content

Commit 8a1df1e

Browse files
Merge pull request #1 from servicetitan/proxy-improvements
Proxy improvements
2 parents 1566e3d + 735fb84 commit 8a1df1e

File tree

3 files changed

+107
-8
lines changed

3 files changed

+107
-8
lines changed

LazyProxy.Unity.Tests/UnityExtensionTests.cs

+61-2
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ public void LifetimeMustBeCorrect()
161161
_service2Id = string.Empty;
162162

163163
var container = new UnityContainer()
164-
.RegisterLazy<IService1, Service1>(() => new SingletonLifetimeManager())
165-
.RegisterLazy<IService2, Service2>(() => new SingletonLifetimeManager());
164+
.RegisterLazy<IService1, Service1>(() => new ContainerControlledLifetimeManager())
165+
.RegisterLazy<IService2, Service2>(() => new ContainerControlledLifetimeManager());
166166

167167
Assert.Empty(_service1Id);
168168
Assert.Empty(_service2Id);
@@ -222,5 +222,64 @@ public void ServicesMustBeResolvedFromChildContainer()
222222

223223
Assert.Null(exception);
224224
}
225+
226+
[Fact]
227+
public void ServicesMustBeResolvedByNameWithCorrectLifetime()
228+
{
229+
const string serviceName1 = "serviceName1";
230+
const string serviceName2 = "serviceName2";
231+
232+
var container = new UnityContainer()
233+
.RegisterLazy<IService1, Service1>(serviceName1, () => new ContainerControlledLifetimeManager())
234+
.RegisterLazy<IService1, Service1>(serviceName2, () => new HierarchicalLifetimeManager());
235+
236+
var childContainer = container.CreateChildContainer();
237+
238+
Assert.Throws<ResolutionFailedException>(() => container.Resolve<IService1>());
239+
Assert.Same(container.Resolve<IService1>(serviceName1), container.Resolve<IService1>(serviceName1));
240+
Assert.Same(container.Resolve<IService1>(serviceName2), container.Resolve<IService1>(serviceName2));
241+
Assert.NotSame(container.Resolve<IService1>(serviceName1), container.Resolve<IService1>(serviceName2));
242+
243+
Assert.Throws<ResolutionFailedException>(() => childContainer.Resolve<IService1>());
244+
Assert.Same(childContainer.Resolve<IService1>(serviceName1), childContainer.Resolve<IService1>(serviceName1));
245+
Assert.Same(childContainer.Resolve<IService1>(serviceName2), childContainer.Resolve<IService1>(serviceName2));
246+
247+
Assert.NotSame(childContainer.Resolve<IService1>(serviceName1), childContainer.Resolve<IService1>(serviceName2));
248+
Assert.Same(container.Resolve<IService1>(serviceName1), childContainer.Resolve<IService1>(serviceName1));
249+
Assert.NotSame(container.Resolve<IService1>(serviceName2), childContainer.Resolve<IService1>(serviceName2));
250+
}
251+
252+
[Fact]
253+
public void ServicesMustBeResolvedByNameWithCorrectInjectionMembers()
254+
{
255+
const string arg = "arg";
256+
const string result1 = "result1";
257+
const string result2 = "result2";
258+
const string serviceName1 = "serviceName1";
259+
const string serviceName2 = "serviceName2";
260+
261+
var serviceMock1 = new Mock<IService2>(MockBehavior.Strict);
262+
serviceMock1.Setup(s => s.Method(arg)).Returns(result1);
263+
264+
var serviceMock2 = new Mock<IService2>(MockBehavior.Strict);
265+
serviceMock2.Setup(s => s.Method(arg)).Returns(result2);
266+
267+
var container = new UnityContainer()
268+
.RegisterLazy<IService1, Service1>(serviceName1, new InjectionConstructor(serviceMock1.Object))
269+
.RegisterLazy<IService1, Service1>(serviceName2, new InjectionConstructor(serviceMock2.Object));
270+
271+
var actualResult1 = container.Resolve<IService1>(serviceName1).MethodWithOtherServiceInvocation(arg);
272+
var actualResult2 = container.Resolve<IService1>(serviceName2).MethodWithOtherServiceInvocation(arg);
273+
274+
Assert.Throws<ResolutionFailedException>(() => container.Resolve<IService1>());
275+
Assert.Equal("service1->" + result1, actualResult1);
276+
Assert.Equal("service1->" + result2, actualResult2);
277+
}
278+
279+
[Fact]
280+
public void RegistrationMustThrowAnExceptionForNonInterfaces()
281+
{
282+
Assert.Throws<NotSupportedException>(() => new UnityContainer().RegisterLazy<Service1, Service1>());
283+
}
225284
}
226285
}

LazyProxy.Unity/LazyProxy.Unity.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<GenerateDocumentationFile>true</GenerateDocumentationFile>
1616
</PropertyGroup>
1717
<ItemGroup>
18-
<PackageReference Include="LazyProxy" Version="0.1.0" />
18+
<PackageReference Include="LazyProxy" Version="0.1.1" />
1919
<PackageReference Include="Unity" Version="5.8.6" />
2020
</ItemGroup>
2121
</Project>

LazyProxy.Unity/UnityExtensions.cs

+45-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ namespace LazyProxy.Unity
88
{
99
public static class UnityExtensions
1010
{
11+
private static readonly Func<LifetimeManager> GetTransientLifetimeManager = () => new TransientLifetimeManager();
12+
1113
/// <summary>
1214
/// Is used to register interface TFrom to class TTo by creation a lazy proxy at runtime.
1315
/// The real class To will be instantiated only after first method execution.
@@ -19,10 +21,24 @@ public static class UnityExtensions
1921
/// <returns>The instance of Unity container.</returns>
2022
public static IUnityContainer RegisterLazy<TFrom, TTo>(this IUnityContainer container,
2123
params InjectionMember[] injectionMembers)
22-
where TTo : TFrom where TFrom : class
23-
{
24-
return container.RegisterLazy<TFrom, TTo>(() => new TransientLifetimeManager(), injectionMembers);
25-
}
24+
where TTo : TFrom where TFrom : class =>
25+
container.RegisterLazy<TFrom, TTo>(null, GetTransientLifetimeManager, injectionMembers);
26+
27+
/// <summary>
28+
/// Is used to register interface TFrom to class TTo by creation a lazy proxy at runtime.
29+
/// The real class To will be instantiated only after first method or property execution.
30+
/// </summary>
31+
/// <param name="container">The instance of Unity container.</param>
32+
/// <param name="name">The registration name.</param>
33+
/// <param name="injectionMembers">The set of injection members.</param>
34+
/// <typeparam name="TFrom">The binded interface.</typeparam>
35+
/// <typeparam name="TTo">The binded class.</typeparam>
36+
/// <returns>The instance of Unity container.</returns>
37+
public static IUnityContainer RegisterLazy<TFrom, TTo>(this IUnityContainer container,
38+
string name,
39+
params InjectionMember[] injectionMembers)
40+
where TTo : TFrom where TFrom : class =>
41+
container.RegisterLazy<TFrom, TTo>(name, GetTransientLifetimeManager, injectionMembers);
2642

2743
/// <summary>
2844
/// Is used to register interface TFrom to class TTo by creation a lazy proxy at runtime.
@@ -35,16 +51,40 @@ public static IUnityContainer RegisterLazy<TFrom, TTo>(this IUnityContainer cont
3551
/// <typeparam name="TTo">The binded class.</typeparam>
3652
/// <returns>The instance of Unity container.</returns>
3753
public static IUnityContainer RegisterLazy<TFrom, TTo>(this IUnityContainer container,
54+
Func<LifetimeManager> getLifetimeManager,
55+
params InjectionMember[] injectionMembers)
56+
where TTo : TFrom where TFrom : class =>
57+
container.RegisterLazy<TFrom, TTo>(null, getLifetimeManager, injectionMembers);
58+
59+
/// <summary>
60+
/// Is used to register interface TFrom to class TTo by creation a lazy proxy at runtime.
61+
/// The real class To will be instantiated only after first method or property execution.
62+
/// </summary>
63+
/// <param name="container">The instance of Unity container.</param>
64+
/// <param name="name">The registration name.</param>
65+
/// <param name="getLifetimeManager">The function instance lifetime provides.</param>
66+
/// <param name="injectionMembers">The set of injection members.</param>
67+
/// <typeparam name="TFrom">The binded interface.</typeparam>
68+
/// <typeparam name="TTo">The binded class.</typeparam>
69+
/// <returns>The instance of Unity container.</returns>
70+
public static IUnityContainer RegisterLazy<TFrom, TTo>(this IUnityContainer container,
71+
string name,
3872
Func<LifetimeManager> getLifetimeManager,
3973
params InjectionMember[] injectionMembers)
4074
where TTo : TFrom where TFrom : class
4175
{
76+
// There is no way to constraint it on the compilation step.
77+
if (!typeof(TFrom).IsInterface)
78+
{
79+
throw new NotSupportedException("The lazy registration is supported only for interfaces.");
80+
}
81+
4282
var lazyProxyType = LazyProxyBuilder.BuildLazyProxyType<TFrom>();
4383
var registrationName = Guid.NewGuid().ToString();
4484

4585
return container
4686
.RegisterType<TFrom, TTo>(registrationName, getLifetimeManager(), injectionMembers)
47-
.RegisterType(typeof(TFrom), lazyProxyType,
87+
.RegisterType(typeof(TFrom), lazyProxyType, name,
4888
getLifetimeManager(),
4989
new InjectionConstructor(
5090
new ResolvedParameter<Lazy<TFrom>>(registrationName))

0 commit comments

Comments
 (0)