Skip to content

Add supporting of opened generic interfaces. #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions LazyProxy.Unity.Tests/UnityExtensionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,62 @@ internal class InternalService : IInternalService
public string Get() => "InternalService";
}

public interface IBaseArgument { }

public abstract class AnotherArgument
{
public string Value { get; set; }
}

public class Argument1A : IBaseArgument { }

public class Argument1B : IBaseArgument { }

public class Argument1C : IBaseArgument { }

public struct Argument2 { }

public class Argument3 : AnotherArgument, IBaseArgument { }

public interface IGenericService<T, in TIn, out TOut>
where T : class, IBaseArgument, new()
where TIn : struct
where TOut : AnotherArgument, IBaseArgument
{
TOut Get<TArg>(T arg1, TIn arg2, TArg arg3) where TArg : struct;
}

private class GenericService<T, TIn, TOut> : IGenericService<T, TIn, TOut>
where T : class, IBaseArgument, new()
where TIn : struct
where TOut : AnotherArgument, IBaseArgument, new()
{
protected virtual string Get() => "";

public TOut Get<TArg>(T arg1, TIn arg2, TArg arg3) where TArg : struct
{
return new TOut
{
Value = $"{arg1.GetType().Name}_{arg2.GetType().Name}_{arg3.GetType().Name}{Get()}"
};
}
}

private class DerivedGenericService<T, TIn, TOut> : GenericService<T, TIn, TOut>
where T : class, IBaseArgument, new()
where TIn : struct
where TOut : AnotherArgument, IBaseArgument, new()
{
private readonly string _value;

protected override string Get() => $"_{_value}";

public DerivedGenericService(string value)
{
_value = value;
}
}

[Fact]
public void ServiceCtorMustBeExecutedAfterMethodIsCalledAndOnlyOnce()
{
Expand Down Expand Up @@ -305,5 +361,131 @@ public void InternalsVisibleToAttributeMustAllowToResolveInternalServices()

Assert.Equal("InternalService", result);
}

[Fact]
public void ClosedGenericServiceMustBeResolved()
{
var result = new UnityContainer()
.RegisterLazy(
typeof(IGenericService<Argument1A, Argument2, Argument3>),
typeof(GenericService<Argument1A, Argument2, Argument3>))
.Resolve<IGenericService<Argument1A, Argument2, Argument3>>()
.Get(new Argument1A(), new Argument2(), 42);

Assert.Equal("Argument1A_Argument2_Int32", result.Value);
}

[Fact]
public void OpenedGenericServiceMustBeResolved()
{
var result = new UnityContainer()
.RegisterLazy(typeof(IGenericService<,,>), typeof(GenericService<,,>))
.Resolve<IGenericService<Argument1A, Argument2, Argument3>>()
.Get(new Argument1A(), new Argument2(), 42);

Assert.Equal("Argument1A_Argument2_Int32", result.Value);
}

[Fact]
public void GenericServiceMustBeResolvedWithCorrectLifetime()
{
var container = new UnityContainer()
.RegisterLazy(
typeof(IGenericService<,,>),
typeof(GenericService<,,>),
() => new TransientLifetimeManager())
.RegisterLazy(
typeof(IGenericService<Argument1A, Argument2, Argument3>),
typeof(GenericService<Argument1A, Argument2, Argument3>),
() => new ContainerControlledLifetimeManager())
.RegisterLazy(
typeof(IGenericService<Argument1B, Argument2, Argument3>),
typeof(GenericService<Argument1B, Argument2, Argument3>),
() => new HierarchicalLifetimeManager())
.RegisterLazy(
typeof(IGenericService<Argument1C, Argument2, Argument3>),
typeof(GenericService<Argument1C, Argument2, Argument3>),
() => new TransientLifetimeManager());

var service1 = container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>();
var service2 = container.Resolve<IGenericService<Argument1B, Argument2, Argument3>>();
var service3 = container.Resolve<IGenericService<Argument1C, Argument2, Argument3>>();

Assert.Same(service1, container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>());
Assert.Same(service2, container.Resolve<IGenericService<Argument1B, Argument2, Argument3>>());
Assert.NotSame(service3, container.Resolve<IGenericService<Argument1C, Argument2, Argument3>>());

using (var childContainer = container.CreateChildContainer())
{
Assert.Same(service1, childContainer.Resolve<IGenericService<Argument1A, Argument2, Argument3>>());
Assert.NotSame(service2, childContainer.Resolve<IGenericService<Argument1B, Argument2, Argument3>>());
Assert.NotSame(service3, childContainer.Resolve<IGenericService<Argument1C, Argument2, Argument3>>());
}
}

[Fact]
public void GenericServicesMustBeResolvedByNameWithCorrectLifetime()
{
const string singleton = "singleton";
const string hierarchical = "hierarchical";
const string transient = "transient";

var container = new UnityContainer()
.RegisterLazy(
typeof(IGenericService<,,>),
typeof(GenericService<,,>),
singleton,
() => new ContainerControlledLifetimeManager())
.RegisterLazy(
typeof(IGenericService<,,>),
typeof(GenericService<,,>),
hierarchical,
() => new HierarchicalLifetimeManager())
.RegisterLazy(
typeof(IGenericService<,,>),
typeof(GenericService<,,>),
transient,
() => new TransientLifetimeManager());

var service1 = container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>(singleton);
var service2 = container.Resolve<IGenericService<Argument1B, Argument2, Argument3>>(hierarchical);
var service3 = container.Resolve<IGenericService<Argument1C, Argument2, Argument3>>(transient);

Assert.Same(service1, container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>(singleton));
Assert.Same(service2, container.Resolve<IGenericService<Argument1B, Argument2, Argument3>>(hierarchical));
Assert.NotSame(service3, container.Resolve<IGenericService<Argument1C, Argument2, Argument3>>(transient));

using (var childContainer = container.CreateChildContainer())
{
Assert.Same(service1, childContainer.Resolve<IGenericService<Argument1A, Argument2, Argument3>>(singleton));
Assert.NotSame(service2, childContainer.Resolve<IGenericService<Argument1B, Argument2, Argument3>>(hierarchical));
Assert.NotSame(service3, childContainer.Resolve<IGenericService<Argument1C, Argument2, Argument3>>(transient));
}
}

[Fact]
public void GenericServicesMustBeResolvedByNameWithCorrectInjectionMembers()
{
const string value1 = "value1";
const string value2 = "value2";

var container = new UnityContainer()
.RegisterLazy(
typeof(IGenericService<,,>),
typeof(DerivedGenericService<,,>),
value1,
new InjectionConstructor(value1))
.RegisterLazy(
typeof(IGenericService<,,>),
typeof(DerivedGenericService<,,>),
value2,
new InjectionConstructor(value2));

var service1 = container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>(value1);
var service2 = container.Resolve<IGenericService<Argument1A, Argument2, Argument3>>(value2);

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);
}
}
}
2 changes: 1 addition & 1 deletion LazyProxy.Unity/LazyProxy.Unity.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LazyProxy" Version="0.1.2" />
<PackageReference Include="LazyProxy" Version="0.1.4" />
<PackageReference Include="Unity" Version="5.8.6" />
</ItemGroup>
</Project>
11 changes: 5 additions & 6 deletions LazyProxy.Unity/UnityExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

namespace LazyProxy.Unity
{
/// <summary>
/// Extension methods for lazy registration.
/// </summary>
public static class UnityExtensions
{
private static readonly Func<LifetimeManager> GetTransientLifetimeManager = () => new TransientLifetimeManager();
Expand Down Expand Up @@ -147,16 +150,12 @@ public static IUnityContainer RegisterLazy(this IUnityContainer container,
throw new NotSupportedException("The lazy registration is supported only for interfaces.");
}

var lazyProxyType = LazyProxyBuilder.BuildLazyProxyType(typeFrom);
var registrationName = Guid.NewGuid().ToString();

return container
.RegisterType(typeFrom, typeTo, registrationName, getLifetimeManager(), injectionMembers)
.RegisterType(typeFrom, lazyProxyType, name,
getLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter(typeof(Lazy<>).MakeGenericType(typeFrom), registrationName))
);
.RegisterType(typeFrom, name, getLifetimeManager(), new InjectionFactory(
(c, t, n) => LazyProxyBuilder.CreateInstance(t, () => c.Resolve(t, registrationName))));
}
}
}