Skip to content

Commit 802bec3

Browse files
Merge pull request #4 from servicetitan/supporting-opened-generic-interfaces
Add supporting of opened generic interfaces.
2 parents 5e2a83e + 79de82e commit 802bec3

File tree

3 files changed

+188
-7
lines changed

3 files changed

+188
-7
lines changed

LazyProxy.Unity.Tests/UnityExtensionTests.cs

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,62 @@ internal class InternalService : IInternalService
7070
public string Get() => "InternalService";
7171
}
7272

73+
public interface IBaseArgument { }
74+
75+
public abstract class AnotherArgument
76+
{
77+
public string Value { get; set; }
78+
}
79+
80+
public class Argument1A : IBaseArgument { }
81+
82+
public class Argument1B : IBaseArgument { }
83+
84+
public class Argument1C : IBaseArgument { }
85+
86+
public struct Argument2 { }
87+
88+
public class Argument3 : AnotherArgument, IBaseArgument { }
89+
90+
public interface IGenericService<T, in TIn, out TOut>
91+
where T : class, IBaseArgument, new()
92+
where TIn : struct
93+
where TOut : AnotherArgument, IBaseArgument
94+
{
95+
TOut Get<TArg>(T arg1, TIn arg2, TArg arg3) where TArg : struct;
96+
}
97+
98+
private class GenericService<T, TIn, TOut> : IGenericService<T, TIn, TOut>
99+
where T : class, IBaseArgument, new()
100+
where TIn : struct
101+
where TOut : AnotherArgument, IBaseArgument, new()
102+
{
103+
protected virtual string Get() => "";
104+
105+
public TOut Get<TArg>(T arg1, TIn arg2, TArg arg3) where TArg : struct
106+
{
107+
return new TOut
108+
{
109+
Value = $"{arg1.GetType().Name}_{arg2.GetType().Name}_{arg3.GetType().Name}{Get()}"
110+
};
111+
}
112+
}
113+
114+
private class DerivedGenericService<T, TIn, TOut> : GenericService<T, TIn, TOut>
115+
where T : class, IBaseArgument, new()
116+
where TIn : struct
117+
where TOut : AnotherArgument, IBaseArgument, new()
118+
{
119+
private readonly string _value;
120+
121+
protected override string Get() => $"_{_value}";
122+
123+
public DerivedGenericService(string value)
124+
{
125+
_value = value;
126+
}
127+
}
128+
73129
[Fact]
74130
public void ServiceCtorMustBeExecutedAfterMethodIsCalledAndOnlyOnce()
75131
{
@@ -305,5 +361,131 @@ public void InternalsVisibleToAttributeMustAllowToResolveInternalServices()
305361

306362
Assert.Equal("InternalService", result);
307363
}
364+
365+
[Fact]
366+
public void ClosedGenericServiceMustBeResolved()
367+
{
368+
var result = new UnityContainer()
369+
.RegisterLazy(
370+
typeof(IGenericService<Argument1A, Argument2, Argument3>),
371+
typeof(GenericService<Argument1A, Argument2, Argument3>))
372+
.Resolve<IGenericService<Argument1A, Argument2, Argument3>>()
373+
.Get(new Argument1A(), new Argument2(), 42);
374+
375+
Assert.Equal("Argument1A_Argument2_Int32", result.Value);
376+
}
377+
378+
[Fact]
379+
public void OpenedGenericServiceMustBeResolved()
380+
{
381+
var result = new UnityContainer()
382+
.RegisterLazy(typeof(IGenericService<,,>), typeof(GenericService<,,>))
383+
.Resolve<IGenericService<Argument1A, Argument2, Argument3>>()
384+
.Get(new Argument1A(), new Argument2(), 42);
385+
386+
Assert.Equal("Argument1A_Argument2_Int32", result.Value);
387+
}
388+
389+
[Fact]
390+
public void GenericServiceMustBeResolvedWithCorrectLifetime()
391+
{
392+
var container = new UnityContainer()
393+
.RegisterLazy(
394+
typeof(IGenericService<,,>),
395+
typeof(GenericService<,,>),
396+
() => new TransientLifetimeManager())
397+
.RegisterLazy(
398+
typeof(IGenericService<Argument1A, Argument2, Argument3>),
399+
typeof(GenericService<Argument1A, Argument2, Argument3>),
400+
() => new ContainerControlledLifetimeManager())
401+
.RegisterLazy(
402+
typeof(IGenericService<Argument1B, Argument2, Argument3>),
403+
typeof(GenericService<Argument1B, Argument2, Argument3>),
404+
() => new HierarchicalLifetimeManager())
405+
.RegisterLazy(
406+
typeof(IGenericService<Argument1C, Argument2, Argument3>),
407+
typeof(GenericService<Argument1C, Argument2, Argument3>),
408+
() => new TransientLifetimeManager());
409+
410+
var service1 = container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>();
411+
var service2 = container.Resolve<IGenericService<Argument1B, Argument2, Argument3>>();
412+
var service3 = container.Resolve<IGenericService<Argument1C, Argument2, Argument3>>();
413+
414+
Assert.Same(service1, container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>());
415+
Assert.Same(service2, container.Resolve<IGenericService<Argument1B, Argument2, Argument3>>());
416+
Assert.NotSame(service3, container.Resolve<IGenericService<Argument1C, Argument2, Argument3>>());
417+
418+
using (var childContainer = container.CreateChildContainer())
419+
{
420+
Assert.Same(service1, childContainer.Resolve<IGenericService<Argument1A, Argument2, Argument3>>());
421+
Assert.NotSame(service2, childContainer.Resolve<IGenericService<Argument1B, Argument2, Argument3>>());
422+
Assert.NotSame(service3, childContainer.Resolve<IGenericService<Argument1C, Argument2, Argument3>>());
423+
}
424+
}
425+
426+
[Fact]
427+
public void GenericServicesMustBeResolvedByNameWithCorrectLifetime()
428+
{
429+
const string singleton = "singleton";
430+
const string hierarchical = "hierarchical";
431+
const string transient = "transient";
432+
433+
var container = new UnityContainer()
434+
.RegisterLazy(
435+
typeof(IGenericService<,,>),
436+
typeof(GenericService<,,>),
437+
singleton,
438+
() => new ContainerControlledLifetimeManager())
439+
.RegisterLazy(
440+
typeof(IGenericService<,,>),
441+
typeof(GenericService<,,>),
442+
hierarchical,
443+
() => new HierarchicalLifetimeManager())
444+
.RegisterLazy(
445+
typeof(IGenericService<,,>),
446+
typeof(GenericService<,,>),
447+
transient,
448+
() => new TransientLifetimeManager());
449+
450+
var service1 = container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>(singleton);
451+
var service2 = container.Resolve<IGenericService<Argument1B, Argument2, Argument3>>(hierarchical);
452+
var service3 = container.Resolve<IGenericService<Argument1C, Argument2, Argument3>>(transient);
453+
454+
Assert.Same(service1, container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>(singleton));
455+
Assert.Same(service2, container.Resolve<IGenericService<Argument1B, Argument2, Argument3>>(hierarchical));
456+
Assert.NotSame(service3, container.Resolve<IGenericService<Argument1C, Argument2, Argument3>>(transient));
457+
458+
using (var childContainer = container.CreateChildContainer())
459+
{
460+
Assert.Same(service1, childContainer.Resolve<IGenericService<Argument1A, Argument2, Argument3>>(singleton));
461+
Assert.NotSame(service2, childContainer.Resolve<IGenericService<Argument1B, Argument2, Argument3>>(hierarchical));
462+
Assert.NotSame(service3, childContainer.Resolve<IGenericService<Argument1C, Argument2, Argument3>>(transient));
463+
}
464+
}
465+
466+
[Fact]
467+
public void GenericServicesMustBeResolvedByNameWithCorrectInjectionMembers()
468+
{
469+
const string value1 = "value1";
470+
const string value2 = "value2";
471+
472+
var container = new UnityContainer()
473+
.RegisterLazy(
474+
typeof(IGenericService<,,>),
475+
typeof(DerivedGenericService<,,>),
476+
value1,
477+
new InjectionConstructor(value1))
478+
.RegisterLazy(
479+
typeof(IGenericService<,,>),
480+
typeof(DerivedGenericService<,,>),
481+
value2,
482+
new InjectionConstructor(value2));
483+
484+
var service1 = container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>(value1);
485+
var service2 = container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>(value2);
486+
487+
Assert.Equal($"Argument1A_Argument2_Int32_{value1}", service1.Get(new Argument1A(), new Argument2(), 42).Value);
488+
Assert.Equal($"Argument1A_Argument2_Int32_{value2}", service2.Get(new Argument1A(), new Argument2(), 42).Value);
489+
}
308490
}
309491
}

LazyProxy.Unity/LazyProxy.Unity.csproj

Lines changed: 1 addition & 1 deletion
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.2" />
18+
<PackageReference Include="LazyProxy" Version="0.1.4" />
1919
<PackageReference Include="Unity" Version="5.8.6" />
2020
</ItemGroup>
2121
</Project>

LazyProxy.Unity/UnityExtensions.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
namespace LazyProxy.Unity
88
{
9+
/// <summary>
10+
/// Extension methods for lazy registration.
11+
/// </summary>
912
public static class UnityExtensions
1013
{
1114
private static readonly Func<LifetimeManager> GetTransientLifetimeManager = () => new TransientLifetimeManager();
@@ -147,16 +150,12 @@ public static IUnityContainer RegisterLazy(this IUnityContainer container,
147150
throw new NotSupportedException("The lazy registration is supported only for interfaces.");
148151
}
149152

150-
var lazyProxyType = LazyProxyBuilder.BuildLazyProxyType(typeFrom);
151153
var registrationName = Guid.NewGuid().ToString();
152154

153155
return container
154156
.RegisterType(typeFrom, typeTo, registrationName, getLifetimeManager(), injectionMembers)
155-
.RegisterType(typeFrom, lazyProxyType, name,
156-
getLifetimeManager(),
157-
new InjectionConstructor(
158-
new ResolvedParameter(typeof(Lazy<>).MakeGenericType(typeFrom), registrationName))
159-
);
157+
.RegisterType(typeFrom, name, getLifetimeManager(), new InjectionFactory(
158+
(c, t, n) => LazyProxyBuilder.CreateInstance(t, () => c.Resolve(t, registrationName))));
160159
}
161160
}
162161
}

0 commit comments

Comments
 (0)