-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
ServiceProviderExtensions.cs
142 lines (126 loc) · 6.36 KB
/
ServiceProviderExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
using System;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
#nullable enable
namespace OrchardCore.Environment.Shell.Builders
{
public static class ServiceProviderExtensions
{
/// <summary>
/// Creates a child container.
/// </summary>
/// <param name="serviceProvider">The service provider to create a child container for.</param>
/// <param name="serviceCollection">The services to clone.</param>
public static IServiceCollection CreateChildContainer(this IServiceProvider serviceProvider, IServiceCollection serviceCollection)
{
IServiceCollection clonedCollection = new ServiceCollection();
var servicesByType = serviceCollection.GroupBy(s => (s.ServiceType, s.ServiceKey));
foreach (var services in servicesByType)
{
// Prevent hosting 'IStartupFilter' to re-add middleware to the tenant pipeline.
if (services.Key.ServiceType == typeof(IStartupFilter))
{
}
// A generic type definition is rather used to create other constructed generic types.
else if (services.Key.ServiceType.IsGenericTypeDefinition)
{
// So, we just need to pass the descriptor.
foreach (var service in services)
{
clonedCollection.Add(service);
}
}
// If only one service of a given type.
else if (services.Count() == 1)
{
var service = services.First();
if (service.Lifetime == ServiceLifetime.Singleton)
{
// An host singleton is shared across tenant containers but only registered instances are not disposed
// by the DI, so we check if it is disposable or if it uses a factory which may return a different type.
if (typeof(IDisposable).IsAssignableFrom(service.GetImplementationType()) ||
service.GetImplementationFactory() is not null)
{
// If disposable, register an instance that we resolve immediately from the main container.
var instance = service.IsKeyedService
? serviceProvider.GetRequiredKeyedService(services.Key.ServiceType, services.Key.ServiceKey)
: serviceProvider.GetRequiredService(services.Key.ServiceType);
clonedCollection.CloneSingleton(service, instance);
}
else if (!service.IsKeyedService)
{
// If not disposable, the singleton can be resolved through a factory when first requested.
clonedCollection.CloneSingleton(service, sp =>
serviceProvider.GetRequiredService(service.ServiceType));
// Note: Most of the time a singleton of a given type is unique and not disposable. So,
// most of the time it will be resolved when first requested through a tenant container.
}
else
{
clonedCollection.CloneSingleton(service, (sp, key) =>
serviceProvider.GetRequiredKeyedService(service.ServiceType, key));
}
}
else
{
clonedCollection.Add(service);
}
}
// If all services of the same type are not singletons.
else if (services.All(s => s.Lifetime != ServiceLifetime.Singleton))
{
// We don't need to resolve them.
foreach (var service in services)
{
clonedCollection.Add(service);
}
}
// If all services of the same type are singletons.
else if (services.All(s => s.Lifetime == ServiceLifetime.Singleton))
{
// We can resolve them from the main container.
var instances = services.Key.ServiceKey is not null
? serviceProvider.GetKeyedServices(services.Key.ServiceType, services.Key.ServiceKey)
: serviceProvider.GetServices(services.Key.ServiceType);
for (var i = 0; i < services.Count(); i++)
{
var instance = instances.ElementAt(i);
if (instance is null)
{
continue;
}
clonedCollection.CloneSingleton(services.ElementAt(i), instance);
}
}
// If singletons and scoped services are mixed.
else
{
// We need a service scope to resolve them.
using var scope = serviceProvider.CreateScope();
var instances = services.Key.ServiceKey is not null
? serviceProvider.GetKeyedServices(services.Key.ServiceType, services.Key.ServiceKey)
: serviceProvider.GetServices(services.Key.ServiceType);
// Then we only keep singleton instances.
for (var i = 0; i < services.Count(); i++)
{
if (services.ElementAt(i).Lifetime == ServiceLifetime.Singleton)
{
var instance = instances.ElementAt(i);
if (instance is null)
{
continue;
}
clonedCollection.CloneSingleton(services.ElementAt(i), instance);
}
else
{
clonedCollection.Add(services.ElementAt(i));
}
}
}
}
return clonedCollection;
}
}
}