Skip to content

Commit 60191a8

Browse files
authored
Merge pull request #216 from TheConstructor/feature/proxy-factory
Proxy-factory to single instance per lifetime, if possible
2 parents ba7738c + a93513d commit 60191a8

8 files changed

+60
-22
lines changed

src/Injectio.Attributes/RegistrationStrategy.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,11 @@ public enum RegistrationStrategy
1616
/// <summary>
1717
/// Registers each matching concrete type as all of its implemented interfaces and itself
1818
/// </summary>
19-
SelfWithInterfaces = 2
19+
SelfWithInterfaces = 2,
20+
/// <summary>
21+
/// Registers each matching concrete type as all of its implemented interfaces and itself.
22+
/// For the interfaces a proxy-factory resolves the service from its type-name, so only one instance is created per lifetime
23+
/// </summary>
24+
/// <remarks>For open-generic registrations, this behaves like <see cref="SelfWithInterfaces"/></remarks>
25+
SelfWithProxyFactory = 3
2026
}

src/Injectio.Generators/KnownTypes.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,29 +37,33 @@ public static class KnownTypes
3737
public const string ServiceLifetimeTransientFullName = "Microsoft.Extensions.DependencyInjection.ServiceLifetime.Transient";
3838

3939

40-
public static readonly int DuplicateStrategySkipValue = 0;
40+
public const int DuplicateStrategySkipValue = 0;
4141
public const string DuplicateStrategySkipShortName = "Skip";
4242
public const string DuplicateStrategySkipTypeName = $"DuplicateStrategy.{DuplicateStrategySkipShortName}";
4343

44-
public static readonly int DuplicateStrategyReplaceValue = 1;
44+
public const int DuplicateStrategyReplaceValue = 1;
4545
public const string DuplicateStrategyReplaceShortName = "Replace";
4646
public const string DuplicateStrategyReplaceTypeName = $"DuplicateStrategy.{DuplicateStrategyReplaceShortName}";
4747

48-
public static readonly int DuplicateStrategyAppendValue = 2;
48+
public const int DuplicateStrategyAppendValue = 2;
4949
public const string DuplicateStrategyAppendShortName = "Append";
5050
public const string DuplicateStrategyAppendTypeName = $"DuplicateStrategy.{DuplicateStrategyAppendShortName}";
5151

5252

53-
public static readonly int RegistrationStrategySelfValue = 0;
53+
public const int RegistrationStrategySelfValue = 0;
5454
public const string RegistrationStrategySelfShortName = "Self";
5555
public const string RegistrationStrategySelfTypeName = $"RegistrationStrategy.{RegistrationStrategySelfShortName}";
5656

57-
public static readonly int RegistrationStrategyImplementedInterfacesValue = 1;
57+
public const int RegistrationStrategyImplementedInterfacesValue = 1;
5858
public const string RegistrationStrategyImplementedInterfacesShortName = "ImplementedInterfaces";
5959
public const string RegistrationStrategyImplementedInterfacesTypeName = $"RegistrationStrategy.{RegistrationStrategyImplementedInterfacesShortName}";
6060

61-
public static readonly int RegistrationStrategySelfWithInterfacesValue = 2;
61+
public const int RegistrationStrategySelfWithInterfacesValue = 2;
6262
public const string RegistrationStrategySelfWithInterfacesShortName = "SelfWithInterfaces";
6363
public const string RegistrationStrategySelfWithInterfacesTypeName = $"RegistrationStrategy.{RegistrationStrategySelfWithInterfacesShortName}";
6464

65+
public const int RegistrationStrategySelfWithProxyFactoryValue = 3;
66+
public const string RegistrationStrategySelfWithProxyFactoryShortName = "SelfWithProxyFactory";
67+
public const string RegistrationStrategySelfWithProxyFactoryTypeName = $"RegistrationStrategy.{RegistrationStrategySelfWithProxyFactoryShortName}";
68+
6569
}

src/Injectio.Generators/ServiceRegistrationGenerator.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ private static (EquatableArray<Diagnostic> diagnostics, bool hasServiceCollectio
336336
&& implementationType == null
337337
&& serviceTypes.Count == 0)
338338
{
339-
registrationStrategy = KnownTypes.RegistrationStrategySelfWithInterfacesShortName;
339+
registrationStrategy = KnownTypes.RegistrationStrategySelfWithProxyFactoryShortName;
340340
}
341341

342342
// no implementation type set, use class attribute is on
@@ -348,7 +348,9 @@ private static (EquatableArray<Diagnostic> diagnostics, bool hasServiceCollectio
348348
}
349349

350350
// add implemented interfaces
351-
bool includeInterfaces = registrationStrategy is KnownTypes.RegistrationStrategyImplementedInterfacesShortName or KnownTypes.RegistrationStrategySelfWithInterfacesShortName;
351+
bool includeInterfaces = registrationStrategy is KnownTypes.RegistrationStrategyImplementedInterfacesShortName
352+
or KnownTypes.RegistrationStrategySelfWithInterfacesShortName
353+
or KnownTypes.RegistrationStrategySelfWithProxyFactoryShortName;
352354
if (includeInterfaces)
353355
{
354356
foreach (var implementedInterface in classSymbol.AllInterfaces)
@@ -366,10 +368,18 @@ private static (EquatableArray<Diagnostic> diagnostics, bool hasServiceCollectio
366368
}
367369

368370
// add class attribute is on; default service type if not set
369-
bool includeSelf = registrationStrategy is KnownTypes.RegistrationStrategySelfShortName or KnownTypes.RegistrationStrategySelfWithInterfacesShortName;
371+
bool includeSelf = registrationStrategy is KnownTypes.RegistrationStrategySelfShortName
372+
or KnownTypes.RegistrationStrategySelfWithInterfacesShortName
373+
or KnownTypes.RegistrationStrategySelfWithProxyFactoryShortName;
370374
if (includeSelf || serviceTypes.Count == 0)
371375
serviceTypes.Add(implementationType!);
372376

377+
if (registrationStrategy is null && serviceTypes.Contains(implementationType!))
378+
registrationStrategy = KnownTypes.RegistrationStrategySelfWithProxyFactoryShortName;
379+
380+
if (registrationStrategy is KnownTypes.RegistrationStrategySelfWithProxyFactoryShortName && isOpenGeneric)
381+
registrationStrategy = KnownTypes.RegistrationStrategySelfWithInterfacesShortName;
382+
373383
return new ServiceRegistration(
374384
Lifetime: serviceLifetime,
375385
ImplementationType: implementationType!,
@@ -529,9 +539,9 @@ private static string ResolveDuplicateStrategy(object? value)
529539
{
530540
int v => v switch
531541
{
532-
0 => KnownTypes.DuplicateStrategySkipShortName,
533-
1 => KnownTypes.DuplicateStrategyReplaceShortName,
534-
2 => KnownTypes.DuplicateStrategyAppendShortName,
542+
KnownTypes.DuplicateStrategySkipValue => KnownTypes.DuplicateStrategySkipShortName,
543+
KnownTypes.DuplicateStrategyReplaceValue => KnownTypes.DuplicateStrategyReplaceShortName,
544+
KnownTypes.DuplicateStrategyAppendValue => KnownTypes.DuplicateStrategyAppendShortName,
535545
_ => KnownTypes.DuplicateStrategySkipShortName
536546
},
537547
string text => text,
@@ -545,13 +555,14 @@ private static string ResolveRegistrationStrategy(object? value)
545555
{
546556
int v => v switch
547557
{
548-
0 => KnownTypes.RegistrationStrategySelfShortName,
549-
1 => KnownTypes.RegistrationStrategyImplementedInterfacesShortName,
550-
2 => KnownTypes.RegistrationStrategySelfWithInterfacesShortName,
551-
_ => KnownTypes.RegistrationStrategySelfWithInterfacesShortName
558+
KnownTypes.RegistrationStrategySelfValue => KnownTypes.RegistrationStrategySelfShortName,
559+
KnownTypes.RegistrationStrategyImplementedInterfacesValue => KnownTypes.RegistrationStrategyImplementedInterfacesShortName,
560+
KnownTypes.RegistrationStrategySelfWithInterfacesValue => KnownTypes.RegistrationStrategySelfWithInterfacesShortName,
561+
KnownTypes.RegistrationStrategySelfWithProxyFactoryValue => KnownTypes.RegistrationStrategySelfWithProxyFactoryShortName,
562+
_ => KnownTypes.RegistrationStrategySelfWithProxyFactoryShortName
552563
},
553564
string text => text,
554-
_ => KnownTypes.RegistrationStrategySelfWithInterfacesShortName
565+
_ => KnownTypes.RegistrationStrategySelfWithProxyFactoryShortName
555566
};
556567
}
557568
}

src/Injectio.Generators/ServiceRegistrationWriter.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,23 @@ private static void WriteServiceGeneric(
291291
.AppendIf(".", !hasNamespace)
292292
.Append(serviceRegistration.Factory);
293293
}
294+
else if (serviceRegistration.Registration == KnownTypes.RegistrationStrategySelfWithProxyFactoryShortName
295+
&& serviceRegistration.ImplementationType != serviceType)
296+
{
297+
codeBuilder
298+
.AppendIf(", ", serviceRegistration.ServiceKey.HasValue())
299+
.Append("(serviceProvider")
300+
.AppendIf(", key", serviceRegistration.ServiceKey.HasValue())
301+
.Append(") => global::Microsoft.Extensions.DependencyInjection.ServiceProvider")
302+
.Append(serviceRegistration.ServiceKey.HasValue()
303+
? "KeyedServiceExtensions.GetRequiredKeyedService<"
304+
: "ServiceExtensions.GetRequiredService<")
305+
.AppendIf("global::", !serviceRegistration.ImplementationType.StartsWith("global::"))
306+
.Append(serviceRegistration.ImplementationType)
307+
.Append(">(serviceProvider")
308+
.AppendIf(", key", serviceRegistration.ServiceKey.HasValue())
309+
.Append(")");
310+
}
294311

295312
codeBuilder
296313
.AppendLine(")")

tests/Injectio.Tests/Snapshots/ServiceRegistrationGeneratorTests.GenerateRegisterScopedSelfWithInterfaces.verified.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection
2020

2121
global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(
2222
serviceCollection,
23-
global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Scoped<global::Injectio.Sample.IService, global::Injectio.Sample.SingletonService>()
23+
global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Scoped<global::Injectio.Sample.IService, global::Injectio.Sample.SingletonService>((serviceProvider) => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<global::Injectio.Sample.SingletonService>(serviceProvider))
2424
);
2525

2626
global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(

tests/Injectio.Tests/Snapshots/ServiceRegistrationGeneratorTests.GenerateRegisterSingletonSelfAsClosedGeneric.verified.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection
2020

2121
global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(
2222
serviceCollection,
23-
global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Singleton<global::Injectio.Sample.IClosedGeneric<object>, global::Injectio.Sample.Service>()
23+
global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Singleton<global::Injectio.Sample.IClosedGeneric<object>, global::Injectio.Sample.Service>((serviceProvider) => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<global::Injectio.Sample.Service>(serviceProvider))
2424
);
2525

2626
global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(

tests/Injectio.Tests/Snapshots/ServiceRegistrationGeneratorTests.GenerateRegisterSingletonTags.verified.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace Microsoft.Extensions.DependencyInjection
2222
{
2323
global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(
2424
serviceCollection,
25-
global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Singleton<global::Injectio.Sample.IServiceTag, global::Injectio.Sample.ServiceTag>()
25+
global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Singleton<global::Injectio.Sample.IServiceTag, global::Injectio.Sample.ServiceTag>((serviceProvider) => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<global::Injectio.Sample.ServiceTag>(serviceProvider))
2626
);
2727

2828
global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(

tests/Injectio.Tests/Snapshots/ServiceRegistrationGeneratorTests.GenerateRegisterTransientSelfWithInterfaces.verified.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection
2020

2121
global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(
2222
serviceCollection,
23-
global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Transient<global::Injectio.Sample.IService, global::Injectio.Sample.SingletonService>()
23+
global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Transient<global::Injectio.Sample.IService, global::Injectio.Sample.SingletonService>((serviceProvider) => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<global::Injectio.Sample.SingletonService>(serviceProvider))
2424
);
2525

2626
global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(

0 commit comments

Comments
 (0)