Skip to content

Commit 20f2d77

Browse files
Implement RegisterGenericFactory without without adding a public source.
1 parent 1caafbf commit 20f2d77

File tree

3 files changed

+166
-69
lines changed

3 files changed

+166
-69
lines changed

LazyProxy.Autofac.Tests/AutofacExtensionTests.cs

+52-25
Original file line numberDiff line numberDiff line change
@@ -383,26 +383,6 @@ public void NonLazyRegistrationMutatorMustBeApplied(string name)
383383
}
384384
}
385385

386-
[Theory]
387-
[InlineData(null)]
388-
[InlineData("name")]
389-
public void RegisterLazyMustAddOpenGenericFactoryRegistrationSourceOnlyOnceForOpenGenericTypes(
390-
string name)
391-
{
392-
var containerBuilder = new ContainerBuilder();
393-
containerBuilder.RegisterLazy(typeof(IGenericService<,,>), typeof(GenericService<,,>), name);
394-
containerBuilder.RegisterLazy(typeof(IGenericService2<>), typeof(GenericService2<>), name);
395-
396-
using (var container = containerBuilder.Build())
397-
{
398-
var count = container.ComponentRegistry.Sources
399-
.OfType<OpenGenericFactoryRegistrationSource>()
400-
.Count();
401-
402-
Assert.Equal(1, count);
403-
}
404-
}
405-
406386
[Theory]
407387
[InlineData(null)]
408388
[InlineData("name")]
@@ -479,6 +459,58 @@ public void InstancePerDependencyServiceLifetimeMustBeActualForOpenGenericServic
479459

480460
#endregion
481461

462+
#region RegisterGenericFactory tests
463+
464+
[Theory]
465+
[InlineData(null)]
466+
[InlineData("name")]
467+
public void RegisterGenericFactoryMustBeCorrect(string name)
468+
{
469+
void Test<T>() where T : IGenericService<ParameterType1, ParameterType2, ParameterType3>
470+
{
471+
var parameters = new Parameter[]
472+
{
473+
new NamedParameter("key1", "value1"),
474+
new NamedParameter("key2", "value2")
475+
};
476+
477+
var containerBuilder = new ContainerBuilder();
478+
containerBuilder.RegisterGenericFactory(typeof(T).GetGenericTypeDefinition(), name,
479+
(c, t, n, p) =>
480+
{
481+
Assert.Same(typeof(T), t);
482+
Assert.Same(name, n);
483+
484+
foreach (var parameter in parameters)
485+
{
486+
Assert.Contains(parameter, p);
487+
}
488+
489+
return new GenericService<ParameterType1, ParameterType2, ParameterType3>();
490+
});
491+
492+
using (var container = containerBuilder.Build())
493+
{
494+
var service = name == null
495+
? container.Resolve<T>(parameters)
496+
: container.ResolveNamed<T>(name, parameters);
497+
498+
var result = service.Get(new ParameterType1(), new ParameterType2(), 42);
499+
500+
Assert.Equal(
501+
$"{typeof(ParameterType1).Name}_" +
502+
$"{typeof(ParameterType2).Name}_" +
503+
$"{typeof(int).Name}",
504+
result.Value);
505+
}
506+
}
507+
508+
Test<IGenericService<ParameterType1, ParameterType2, ParameterType3>>();
509+
Test<GenericService<ParameterType1, ParameterType2, ParameterType3>>();
510+
}
511+
512+
#endregion
513+
482514
#region Private members
483515

484516
[ThreadStatic] private static string _service1Id;
@@ -841,11 +873,6 @@ public interface IGenericService2<T>
841873
T Get(T arg1);
842874
}
843875

844-
private class GenericService2<T> : IGenericService2<T>
845-
{
846-
public T Get(T arg1) => arg1;
847-
}
848-
849876
#endregion
850877
}
851878
}

LazyProxy.Autofac/AutofacExtensions.cs

+73-23
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Reflection;
45
using Autofac;
56
using Autofac.Builder;
67
using Autofac.Core;
8+
using Autofac.Core.Activators.Delegate;
79

810
namespace LazyProxy.Autofac
911
{
@@ -12,8 +14,24 @@ namespace LazyProxy.Autofac
1214
/// </summary>
1315
public static class AutofacExtensions
1416
{
15-
private const string OpenGenericFactoryRegistrationSourceIsAlreadyAdded =
16-
nameof(OpenGenericFactoryRegistrationSourceIsAlreadyAdded);
17+
private static readonly ConstructorInfo RegistrationBuilderConstructor;
18+
19+
static AutofacExtensions()
20+
{
21+
// There is no way to create RegistrationBuilder with TypedService / KeyedService without reflection.
22+
RegistrationBuilderConstructor = typeof(ILifetimeScope).Assembly
23+
.GetType("Autofac.Builder.RegistrationBuilder`3")
24+
.MakeGenericType(
25+
typeof(object),
26+
typeof(SimpleActivatorData),
27+
typeof(SingleRegistrationStyle))
28+
.GetConstructor(new[]
29+
{
30+
typeof(Service),
31+
typeof(SimpleActivatorData),
32+
typeof(SingleRegistrationStyle)
33+
});
34+
}
1735

1836
/// <summary>
1937
/// Is used to register interface TFrom to class TTo by creation a lazy proxy at runtime.
@@ -52,43 +70,75 @@ public static IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrati
5270
}
5371

5472
var registrationName = Guid.NewGuid().ToString();
73+
IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrationStyle> registration;
5574

5675
if (typeTo.IsGenericTypeDefinition)
5776
{
58-
if (!builder.Properties.ContainsKey(OpenGenericFactoryRegistrationSourceIsAlreadyAdded))
59-
{
60-
builder.RegisterSource<OpenGenericFactoryRegistrationSource>();
61-
builder.Properties.Add(OpenGenericFactoryRegistrationSourceIsAlreadyAdded, true);
62-
}
63-
6477
var nonLazyRegistration = builder.RegisterGeneric(typeTo).Named(registrationName, typeFrom);
6578
nonLazyRegistrationMutator?.Mutate(nonLazyRegistration);
79+
80+
registration = builder.RegisterGenericFactory(typeFrom, name,
81+
(c, t, n, p) => CreateLazyProxy(c, t, registrationName, p));
6682
}
6783
else
6884
{
6985
var nonLazyRegistration = builder.RegisterType(typeTo).Named(registrationName, typeFrom);
7086
nonLazyRegistrationMutator?.Mutate(nonLazyRegistration);
71-
}
72-
73-
var registration = builder.Register((c, p) =>
74-
{
75-
var parameters = p.ToList();
76-
var context = c.Resolve<IComponentContext>();
77-
var serviceType = GetServiceType(typeFrom, parameters);
7887

79-
return LazyProxyBuilder.CreateInstance(serviceType,
80-
() => context.ResolveNamed(registrationName, serviceType, parameters)
81-
);
82-
});
88+
registration = builder.Register(
89+
(c, p) => CreateLazyProxy(c.Resolve<IComponentContext>(), typeFrom, registrationName, p));
90+
}
8391

8492
return name == null
8593
? registration.As(typeFrom)
8694
: registration.Named(name, typeFrom);
8795
}
8896

89-
private static Type GetServiceType(Type type, IEnumerable<Parameter> parameters) =>
90-
type.IsGenericType && !type.IsConstructedGenericType
91-
? parameters.Named<Type>(OpenGenericFactoryRegistrationSource.ServiceType)
92-
: type;
97+
/// <summary>
98+
/// Registers a delegate as a component for open generic types.
99+
/// </summary>
100+
/// <param name="builder">The instance of the Autofac container builder.</param>
101+
/// <param name="type"><see cref="Type"/> of the registered component.</param>
102+
/// <param name="name">Name of the registered component.</param>
103+
/// <param name="factory">The delegate to register.</param>
104+
/// <returns>Registration builder allowing the registration to be configured.</returns>
105+
public static IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrationStyle>
106+
RegisterGenericFactory(this ContainerBuilder builder, Type type, string name,
107+
Func<IComponentContext, Type, string, Parameter[], object> factory)
108+
{
109+
var registration = (IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrationStyle>)
110+
RegistrationBuilderConstructor.Invoke(new[]
111+
{
112+
string.IsNullOrEmpty(name)
113+
? (object) new TypedService(type)
114+
: new KeyedService(name, type),
115+
116+
new SimpleActivatorData(new DelegateActivator(type,
117+
(c, p) =>
118+
{
119+
var parameters = p.ToArray();
120+
var serviceType = parameters.Named<Type>(OpenGenericFactoryRegistrationSource.ServiceType);
121+
var context = c.Resolve<IComponentContext>();
122+
123+
return factory(context, serviceType, name, parameters);
124+
})),
125+
126+
new SingleRegistrationStyle()
127+
});
128+
129+
registration.RegistrationData.DeferredCallback = builder.RegisterCallback(
130+
cr => cr.AddRegistrationSource(
131+
new OpenGenericFactoryRegistrationSource(
132+
registration.RegistrationData,
133+
registration.ActivatorData)));
134+
135+
return registration;
136+
}
137+
138+
private static object CreateLazyProxy(
139+
IComponentContext context, Type type, string name, IEnumerable<Parameter> parameters) =>
140+
LazyProxyBuilder.CreateInstance(type,
141+
() => context.ResolveNamed(name, type, parameters)
142+
);
93143
}
94144
}

LazyProxy.Autofac/OpenGenericFactoryRegistrationSource.cs

+41-21
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Reflection;
55
using Autofac;
6+
using Autofac.Builder;
67
using Autofac.Core;
78
using Autofac.Core.Activators.Delegate;
89
using Autofac.Core.Lifetime;
@@ -15,6 +16,9 @@ namespace LazyProxy.Autofac
1516
/// </summary>
1617
public sealed class OpenGenericFactoryRegistrationSource : IRegistrationSource
1718
{
19+
private readonly RegistrationData _registrationData;
20+
private readonly SimpleActivatorData _activatorData;
21+
1822
/// <summary>
1923
/// Name of the key to get the closed generic type from the named parameters.
2024
/// </summary>
@@ -23,6 +27,19 @@ public sealed class OpenGenericFactoryRegistrationSource : IRegistrationSource
2327
/// <inheritdoc />
2428
public bool IsAdapterForIndividualComponents => false;
2529

30+
/// <summary>
31+
/// Creates a new instance of <see cref="OpenGenericFactoryRegistrationSource"/>.
32+
/// </summary>
33+
/// <param name="registrationData">Registration data.</param>
34+
/// <param name="activatorData">Activator data.</param>
35+
public OpenGenericFactoryRegistrationSource(
36+
RegistrationData registrationData,
37+
SimpleActivatorData activatorData)
38+
{
39+
_registrationData = registrationData ?? throw new ArgumentNullException(nameof(registrationData));
40+
_activatorData = activatorData ?? throw new ArgumentNullException(nameof(activatorData));
41+
}
42+
2643
/// <inheritdoc />
2744
public IEnumerable<IComponentRegistration> RegistrationsFor(
2845
Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
@@ -43,29 +60,32 @@ public IEnumerable<IComponentRegistration> RegistrationsFor(
4360
}
4461

4562
var definitionService = (IServiceWithType) swt.ChangeType(swt.ServiceType.GetGenericTypeDefinition());
46-
var factoryRegistrations = registrationAccessor((Service) definitionService);
4763

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) =>
64+
if (!_registrationData.Services.Cast<IServiceWithType>().Any(s => s.Equals(definitionService)))
65+
{
66+
return Enumerable.Empty<IComponentRegistration>();
67+
}
68+
69+
return new[]
70+
{
71+
new ComponentRegistration(
72+
Guid.NewGuid(),
73+
new DelegateActivator(swt.ServiceType, (c, parameters) =>
74+
{
75+
var activator = (DelegateActivator) _activatorData.Activator;
76+
var newParameters = parameters.Concat(new[]
5577
{
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-
});
78+
new NamedParameter(ServiceType, swt.ServiceType)
79+
});
80+
81+
return activator.ActivateInstance(c, newParameters);
82+
}),
83+
GetLifetime(_registrationData.Lifetime),
84+
_registrationData.Sharing,
85+
_registrationData.Ownership,
86+
new[] {service},
87+
new Dictionary<string, object>())
88+
};
6989
}
7090

7191
private static IComponentLifetime GetLifetime(IComponentLifetime lifetime)

0 commit comments

Comments
 (0)