Skip to content

Commit 969c8f5

Browse files
Add supporting of open generic types.
1 parent b80ff44 commit 969c8f5

File tree

3 files changed

+145
-26
lines changed

3 files changed

+145
-26
lines changed

LazyProxy.Autofac.Tests/AutofacExtensionTests.cs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -404,11 +404,9 @@ public void InstancePerDependencyServiceLifetimeMustBeActualForOpenGenericServic
404404

405405
#region Private members
406406

407-
[ThreadStatic]
408-
private static string _service1Id;
407+
[ThreadStatic] private static string _service1Id;
409408

410-
[ThreadStatic]
411-
private static string _service2Id;
409+
[ThreadStatic] private static string _service2Id;
412410

413411
private const string Service1PropertyValue = nameof(Service1PropertyValue);
414412
private const string Service1MethodValue = nameof(Service1MethodValue);
@@ -456,7 +454,8 @@ private static void AssertSingleInstanceServiceLifetimeMustBeActual<T>(Type type
456454
var containerBuilder = new ContainerBuilder();
457455
containerBuilder.RegisterLazy(typeFrom, typeTo, name).SingleInstance();
458456

459-
using (var container = containerBuilder.Build()) {
457+
using (var container = containerBuilder.Build())
458+
{
460459
var resolves = GetResolves<T>(container, name);
461460
var rootContainerResolves = resolves.RootContainerResolves;
462461
var childContainer1Resolves = resolves.Scope1Resolves;
@@ -479,7 +478,8 @@ private static void AssertInstancePerLifetimeScopeServiceLifetimeMustBeActual<T>
479478
var containerBuilder = new ContainerBuilder();
480479
containerBuilder.RegisterLazy(typeFrom, typeTo, name).InstancePerLifetimeScope();
481480

482-
using (var container = containerBuilder.Build()) {
481+
using (var container = containerBuilder.Build())
482+
{
483483
var resolves = GetResolves<T>(container, name);
484484
var rootContainerResolves = resolves.RootContainerResolves;
485485
var childContainer1Resolves = resolves.Scope1Resolves;
@@ -511,7 +511,8 @@ private static void AssertInstancePerDependencyServiceLifetimeMustBeActual<T>(
511511
var containerBuilder = new ContainerBuilder();
512512
containerBuilder.RegisterLazy(typeFrom, typeTo, name).InstancePerDependency();
513513

514-
using (var container = containerBuilder.Build()) {
514+
using (var container = containerBuilder.Build())
515+
{
515516
var resolves = GetResolves<T>(container, name)
516517
.Where(id => id != Guid.Empty)
517518
.ToArray();
@@ -521,21 +522,24 @@ private static void AssertInstancePerDependencyServiceLifetimeMustBeActual<T>(
521522
}
522523

523524
private static Resolves GetResolves<T>(ILifetimeScope container, string name) =>
524-
new Resolves {
525+
new Resolves
526+
{
525527
RootContainerResolves = GetContainerResolves<T>(container, name),
526528
Scope1Resolves = GetScopeResolves<T>(container, name),
527529
Scope2Resolves = GetScopeResolves<T>(container, name)
528530
};
529531

530532
private static SingleContainerResolves GetContainerResolves<T>(IComponentContext container, string name) =>
531-
new SingleContainerResolves {
533+
new SingleContainerResolves
534+
{
532535
Resolve1 = GetResolvedId<T>(container, name),
533536
Resolve2 = GetResolvedId<T>(container, name),
534537
};
535538

536539
private static SingleContainerResolves GetScopeResolves<T>(ILifetimeScope container, string name)
537540
{
538-
using (var scope = container.BeginLifetimeScope()) {
541+
using (var scope = container.BeginLifetimeScope())
542+
{
539543
return GetContainerResolves<T>(scope, name);
540544
}
541545
}
@@ -561,7 +565,9 @@ public interface IHasId
561565
}
562566

563567
// ReSharper disable once MemberCanBePrivate.Global
564-
public interface IService : IHasId { }
568+
public interface IService : IHasId
569+
{
570+
}
565571

566572
// ReSharper disable once MemberCanBePrivate.Global
567573
public class Service : IService
@@ -671,21 +677,29 @@ public ServiceToTestOverrides(SomeEnum someEnum, IService2 service, Argument arg
671677
}
672678
}
673679

674-
public interface IParameterType { }
680+
public interface IParameterType
681+
{
682+
}
675683

676684
public abstract class ParameterTypeBase
677685
{
678686
public string Value { get; set; }
679687
}
680688

681689
// ReSharper disable once MemberCanBePrivate.Global
682-
public class ParameterType1 : IParameterType { }
690+
public class ParameterType1 : IParameterType
691+
{
692+
}
683693

684694
// ReSharper disable once MemberCanBePrivate.Global
685-
public struct ParameterType2 { }
695+
public struct ParameterType2
696+
{
697+
}
686698

687699
// ReSharper disable once MemberCanBePrivate.Global
688-
public class ParameterType3 : ParameterTypeBase, IParameterType { }
700+
public class ParameterType3 : ParameterTypeBase, IParameterType
701+
{
702+
}
689703

690704
// ReSharper disable once MemberCanBePrivate.Global
691705
// ReSharper disable once TypeParameterCanBeVariant

LazyProxy.Autofac/AutofacExtensions.cs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
24
using Autofac;
35
using Autofac.Builder;
6+
using Autofac.Core;
47

58
namespace LazyProxy.Autofac
69
{
@@ -14,8 +17,8 @@ public static class AutofacExtensions
1417
/// The real class To will be instantiated only after first method or property execution.
1518
/// </summary>
1619
/// <param name="builder">The instance of the Autofac container builder.</param>
17-
/// <typeparam name="TFrom">The binded interface.</typeparam>
18-
/// <typeparam name="TTo">The binded class.</typeparam>
20+
/// <typeparam name="TFrom">The linked interface.</typeparam>
21+
/// <typeparam name="TTo">The linked class.</typeparam>
1922
/// <returns>The instance of the Autofac registration builder.</returns>
2023
public static IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrationStyle>
2124
RegisterLazy<TFrom, TTo>(this ContainerBuilder builder)
@@ -28,8 +31,8 @@ public static IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrati
2831
/// </summary>
2932
/// <param name="builder">The instance of the Autofac container builder.</param>
3033
/// <param name="name">The registration name. Null if named registration is not required.</param>
31-
/// <typeparam name="TFrom">The binded interface.</typeparam>
32-
/// <typeparam name="TTo">The binded class.</typeparam>
34+
/// <typeparam name="TFrom">The linked interface.</typeparam>
35+
/// <typeparam name="TTo">The linked class.</typeparam>
3336
/// <returns>The instance of the Autofac registration builder.</returns>
3437
public static IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrationStyle>
3538
RegisterLazy<TFrom, TTo>(this ContainerBuilder builder, string name)
@@ -40,8 +43,8 @@ public static IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrati
4043
/// Is used to register interface TFrom to class TTo by creation a lazy proxy at runtime.
4144
/// The real class To will be instantiated only after first method execution.
4245
/// </summary>
43-
/// <param name="typeFrom">The binded interface.</param>
44-
/// <param name="typeTo">The binded class.</param>
46+
/// <param name="typeFrom">The linked interface.</param>
47+
/// <param name="typeTo">The linked class.</param>
4548
/// <param name="builder">The instance of the Autofac container builder.</param>
4649
/// <returns>The instance of the Autofac registration builder.</returns>
4750
public static IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrationStyle>
@@ -52,8 +55,8 @@ public static IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrati
5255
/// Is used to register interface TFrom to class TTo by creation a lazy proxy at runtime.
5356
/// The real class To will be instantiated only after first method or property execution.
5457
/// </summary>
55-
/// <param name="typeFrom">The binded interface.</param>
56-
/// <param name="typeTo">The binded class.</param>
58+
/// <param name="typeFrom">The linked interface.</param>
59+
/// <param name="typeTo">The linked class.</param>
5760
/// <param name="builder">The instance of the Autofac container builder.</param>
5861
/// <param name="name">The registration name. Null if named registration is not required.</param>
5962
/// <returns>The instance of the Autofac registration builder.</returns>
@@ -66,22 +69,38 @@ public static IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrati
6669
throw new NotSupportedException("The lazy registration is supported only for interfaces.");
6770
}
6871

72+
builder.RegisterSource<OpenGenericFactoryRegistrationSource>();
73+
6974
var registrationName = Guid.NewGuid().ToString();
7075

71-
builder.RegisterType(typeTo).Named(registrationName, typeFrom);
76+
if (typeTo.IsGenericTypeDefinition)
77+
{
78+
builder.RegisterGeneric(typeTo).Named(registrationName, typeFrom);
79+
}
80+
else
81+
{
82+
builder.RegisterType(typeTo).Named(registrationName, typeFrom);
83+
}
7284

7385
var registration = builder.Register((c, p) =>
7486
{
87+
var parameters = p.ToList();
7588
var context = c.Resolve<IComponentContext>();
89+
var serviceType = GetServiceType(typeFrom, parameters);
7690

77-
return LazyProxyBuilder.CreateInstance(typeFrom,
78-
() => context.ResolveNamed(registrationName, typeFrom, p)
91+
return LazyProxyBuilder.CreateInstance(serviceType,
92+
() => context.ResolveNamed(registrationName, serviceType, parameters)
7993
);
8094
});
8195

8296
return name == null
8397
? registration.As(typeFrom)
8498
: registration.Named(name, typeFrom);
8599
}
100+
101+
private static Type GetServiceType(Type type, IEnumerable<Parameter> parameters) =>
102+
type.IsGenericType && !type.IsConstructedGenericType
103+
? parameters.Named<Type>(OpenGenericFactoryRegistrationSource.ServiceType)
104+
: type;
86105
}
87106
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using Autofac;
6+
using Autofac.Core;
7+
using Autofac.Core.Activators.Delegate;
8+
using Autofac.Core.Lifetime;
9+
using Autofac.Core.Registration;
10+
11+
namespace LazyProxy.Autofac
12+
{
13+
/// <summary>
14+
/// Generates registrations for open generic factories.
15+
/// </summary>
16+
public sealed class OpenGenericFactoryRegistrationSource : IRegistrationSource
17+
{
18+
/// <summary>
19+
/// Name of the key to get the closed generic type from the named parameters.
20+
/// </summary>
21+
public const string ServiceType = "ServiceType";
22+
23+
/// <inheritdoc />
24+
public bool IsAdapterForIndividualComponents => false;
25+
26+
/// <inheritdoc />
27+
public IEnumerable<IComponentRegistration> RegistrationsFor(
28+
Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
29+
{
30+
if (service == null)
31+
{
32+
throw new ArgumentNullException(nameof(service));
33+
}
34+
35+
if (registrationAccessor == null)
36+
{
37+
throw new ArgumentNullException(nameof(registrationAccessor));
38+
}
39+
40+
if (!(service is IServiceWithType swt) || !swt.ServiceType.GetTypeInfo().IsGenericType)
41+
{
42+
return Enumerable.Empty<IComponentRegistration>();
43+
}
44+
45+
var definitionService = (IServiceWithType) swt.ChangeType(swt.ServiceType.GetGenericTypeDefinition());
46+
var factoryRegistrations = registrationAccessor((Service) definitionService);
47+
48+
return factoryRegistrations
49+
.Where(factoryRegistration => factoryRegistration.Activator is DelegateActivator)
50+
.Select(factoryRegistration =>
51+
{
52+
return new ComponentRegistration(
53+
Guid.NewGuid(),
54+
new DelegateActivator(swt.ServiceType, (c, parameters) =>
55+
{
56+
var activator = (DelegateActivator) factoryRegistration.Activator;
57+
var newParameters = parameters.Concat(new[]
58+
{
59+
new NamedParameter(ServiceType, swt.ServiceType)
60+
});
61+
return activator.ActivateInstance(c, newParameters);
62+
}),
63+
GetLifetime(factoryRegistration.Lifetime),
64+
factoryRegistration.Sharing,
65+
factoryRegistration.Ownership,
66+
new[] {service},
67+
new Dictionary<string, object>());
68+
});
69+
}
70+
71+
private static IComponentLifetime GetLifetime(IComponentLifetime lifetime)
72+
{
73+
if (lifetime.GetType() == typeof(CurrentScopeLifetime))
74+
{
75+
return new CurrentScopeLifetime();
76+
}
77+
78+
if (lifetime.GetType() == typeof(RootScopeLifetime))
79+
{
80+
return new RootScopeLifetime();
81+
}
82+
83+
throw new NotSupportedException($"Lifetime scope '${lifetime.GetType().Name}' is not supported.");
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)