-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathDependencyEngine.cs
More file actions
542 lines (500 loc) · 22.2 KB
/
DependencyEngine.cs
File metadata and controls
542 lines (500 loc) · 22.2 KB
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
// =================================================================================================================================
// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
// =================================================================================================================================
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using RapidField.SolidInstruments.Core;
using RapidField.SolidInstruments.Core.ArgumentValidation;
using RapidField.SolidInstruments.Core.Extensions;
using System;
using System.Diagnostics;
using System.Threading;
namespace RapidField.SolidInstruments.InversionOfControl
{
/// <summary>
/// Represents a configurable dependency resolution system.
/// </summary>
/// <typeparam name="TConfigurator">
/// The type of the object that configures containers.
/// </typeparam>
/// <typeparam name="TServiceInjector">
/// The type of the object that adds service descriptors to containers.
/// </typeparam>
public abstract class DependencyEngine<TConfigurator, TServiceInjector> : DependencyEngine<TConfigurator>
where TConfigurator : class, new()
where TServiceInjector : class, IServiceInjector<TConfigurator>
{
/// <summary>
/// Initializes a new instance of the <see cref="DependencyEngine{TConfigurator, TServiceInjector}" /> class.
/// </summary>
/// <param name="applicationConfiguration">
/// Configuration information for the application.
/// </param>
/// <param name="package">
/// A package that configures dependencies for the engine.
/// </param>
/// <param name="serviceDescriptors">
/// A collection of service descriptors to which the engine will supply dependencies.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="applicationConfiguration" /> is <see langword="null" /> -or- <paramref name="package" /> is
/// <see langword="null" /> -or- <paramref name="serviceDescriptors" /> is <see langword="null" /> .
/// </exception>
protected DependencyEngine(IConfiguration applicationConfiguration, IDependencyPackage<TConfigurator> package, IServiceCollection serviceDescriptors)
: base(applicationConfiguration, package)
{
LazyProviderFactory = new Lazy<ServiceProviderFactory>(CreateProviderFactory, LazyThreadSafetyMode.ExecutionAndPublication);
ServiceDescriptors = serviceDescriptors.RejectIf().IsNull(nameof(serviceDescriptors)).TargetArgument;
}
/// <summary>
/// Initializes and configures all of the engine's components and enforces operational state.
/// </summary>
/// <exception cref="CreateDependencyScopeException">
/// An exception was raised while attempting to create a new scope.
/// </exception>
/// <exception cref="InvalidOperationException">
/// The engine is in a corrupt state.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// The engine and/or its container are disposed.
/// </exception>
[DebuggerHidden]
internal sealed override void Start()
{
try
{
base.Start();
if (ProviderFactory is null)
{
throw new InvalidOperationException("The dependency engine is in a corrupt state. The provider factory is a null reference.");
}
}
catch (InvalidOperationException)
{
throw;
}
catch (Exception exception)
{
throw new InvalidOperationException("The dependency engine is in a corrupt state. See inner exception for details.", exception);
}
}
/// <summary>
/// Creates a new service provider.
/// </summary>
/// <returns>
/// A new service provider.
/// </returns>
protected sealed override IServiceProvider CreateProvider() => ProviderFactory.CreateServiceProvider(Nix.Instance);
/// <summary>
/// Creates a new service injector.
/// </summary>
/// <returns>
/// A new service injector.
/// </returns>
protected sealed override IServiceInjector<TConfigurator> CreateServiceInjector() => CreateServiceInjector(ServiceDescriptors);
/// <summary>
/// Creates a new service injector.
/// </summary>
/// <param name="serviceDescriptors">
/// A collection of service descriptors that are injected to a configurator.
/// </param>
/// <returns>
/// A new service injector.
/// </returns>
protected abstract TServiceInjector CreateServiceInjector(IServiceCollection serviceDescriptors);
/// <summary>
/// Releases all resources consumed by the current <see cref="DependencyEngine{TConfigurator, TServiceInjector}" />.
/// </summary>
/// <param name="disposing">
/// A value indicating whether or not managed resources should be released.
/// </param>
protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
/// <summary>
/// Creates a new service provider factory.
/// </summary>
/// <returns>
/// A new service provider factory.
/// </returns>
[DebuggerHidden]
private ServiceProviderFactory CreateProviderFactory()
{
var providerFactory = new ServiceProviderFactory(Container);
ServiceDescriptors.AddSingleton<IServiceProviderFactory<Nix>>(providerFactory);
return providerFactory;
}
/// <summary>
/// Gets the engine's service provider factory.
/// </summary>
protected ServiceProviderFactory ProviderFactory => LazyProviderFactory.Value;
/// <summary>
/// Represents a lazily-initialized service provider factory.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Lazy<ServiceProviderFactory> LazyProviderFactory;
/// <summary>
/// Represents a collection of service descriptors to which the engine will supply dependencies.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly IServiceCollection ServiceDescriptors;
}
/// <summary>
/// Represents a configurable dependency resolution system.
/// </summary>
/// <typeparam name="TConfigurator">
/// The type of the object that configures containers.
/// </typeparam>
public abstract class DependencyEngine<TConfigurator> : DependencyEngine
where TConfigurator : class, new()
{
/// <summary>
/// Initializes a new instance of the <see cref="DependencyEngine{TConfigurator}" /> class.
/// </summary>
/// <param name="applicationConfiguration">
/// Configuration information for the application.
/// </param>
/// <param name="package">
/// A package that configures dependencies for the engine.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="applicationConfiguration" /> is <see langword="null" /> -or- <paramref name="package" /> is
/// <see langword="null" />.
/// </exception>
protected DependencyEngine(IConfiguration applicationConfiguration, IDependencyPackage<TConfigurator> package)
: base()
{
ApplicationConfiguration = applicationConfiguration.RejectIf().IsNull(nameof(applicationConfiguration)).TargetArgument;
ConfigureContainerAction = (configuration, configurator) => ConfigureContainer(configuration, configurator);
LazyServiceInjector = new Lazy<IServiceInjector<TConfigurator>>(CreateServiceInjector, LazyThreadSafetyMode.ExecutionAndPublication);
Package = package.RejectIf().IsNull(nameof(package)).TargetArgument;
}
/// <summary>
/// Initializes and configures all of the engine's components and enforces operational state.
/// </summary>
/// <exception cref="CreateDependencyScopeException">
/// An exception was raised while attempting to create a new scope.
/// </exception>
/// <exception cref="InvalidOperationException">
/// The engine is in a corrupt state.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// The engine and/or its container are disposed.
/// </exception>
[DebuggerHidden]
internal override void Start()
{
try
{
base.Start();
if (ServiceInjector is null)
{
throw new InvalidOperationException("The dependency engine is in a corrupt state. The service injector is a null reference.");
}
}
catch (InvalidOperationException)
{
throw;
}
catch (Exception exception)
{
throw new InvalidOperationException("The dependency engine is in a corrupt state. See inner exception for details.", exception);
}
}
/// <summary>
/// Creates a new dependency container.
/// </summary>
/// <returns>
/// A new dependency container.
/// </returns>
/// <exception cref="ContainerConfigurationException">
/// An exception was raised while configuring the container.
/// </exception>
protected sealed override IDependencyContainer CreateContainer()
{
try
{
return CreateContainer(ApplicationConfiguration, ServiceInjector, ConfigureContainerAction);
}
catch (ContainerConfigurationException)
{
throw;
}
catch (Exception exception)
{
throw new ContainerConfigurationException(exception);
}
}
/// <summary>
/// Creates a new dependency container.
/// </summary>
/// <param name="applicationConfiguration">
/// Configuration information for the application.
/// </param>
/// <param name="serviceInjector">
/// An object that injects service descriptors into a container configurator.
/// </param>
/// <param name="configureAction">
/// An action that configures the container.
/// </param>
/// <returns>
/// A new dependency container.
/// </returns>
protected abstract IDependencyContainer CreateContainer(IConfiguration applicationConfiguration, IServiceInjector<TConfigurator> serviceInjector, Action<IConfiguration, TConfigurator> configureAction);
/// <summary>
/// Creates a new service injector.
/// </summary>
/// <returns>
/// A new service injector.
/// </returns>
protected virtual IServiceInjector<TConfigurator> CreateServiceInjector() => new NullOperationServiceInjector<TConfigurator>();
/// <summary>
/// Releases all resources consumed by the current <see cref="DependencyEngine{TConfigurator}" />.
/// </summary>
/// <param name="disposing">
/// A value indicating whether or not managed resources should be released.
/// </param>
protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
/// <summary>
/// Configures a new container.
/// </summary>
/// <param name="applicationConfiguration">
/// Configuration information for the application.
/// </param>
/// <param name="configurator">
/// An object that configures the container.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="applicationConfiguration" /> is <see langword="null" /> -or- <paramref name="configurator" /> is
/// <see langword="null" />.
/// </exception>
/// <exception cref="ContainerConfigurationException">
/// An exception was raised while attempting to configure the container.
/// </exception>
[DebuggerHidden]
private void ConfigureContainer(IConfiguration applicationConfiguration, TConfigurator configurator)
{
var modules = Package.GetModules(applicationConfiguration);
foreach (var module in modules)
{
module.Configure(configurator);
}
}
/// <summary>
/// Gets the engine's service injector.
/// </summary>
protected IServiceInjector<TConfigurator> ServiceInjector => LazyServiceInjector.Value;
/// <summary>
/// Represents configuration information for the application.
/// </summary>
protected readonly IConfiguration ApplicationConfiguration;
/// <summary>
/// Represents an action that configures a container.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Action<IConfiguration, TConfigurator> ConfigureContainerAction;
/// <summary>
/// Represents a lazily-initialized service injector.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Lazy<IServiceInjector<TConfigurator>> LazyServiceInjector;
/// <summary>
/// Represents a package that configures dependencies for the engine.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly IDependencyPackage<TConfigurator> Package;
}
/// <summary>
/// Represents a configurable dependency resolution system.
/// </summary>
/// <remarks>
/// <see cref="DependencyEngine" /> is the default implementation of <see cref="IDependencyEngine" />.
/// </remarks>
public abstract class DependencyEngine : Instrument, IDependencyEngine
{
/// <summary>
/// Initializes a new instance of the <see cref="DependencyEngine" /> class.
/// </summary>
protected DependencyEngine()
: base()
{
LazyContainer = new Lazy<IDependencyContainer>(CreateContainer, LazyThreadSafetyMode.ExecutionAndPublication);
LazyProvider = new Lazy<IServiceProvider>(CreateProvider, LazyThreadSafetyMode.ExecutionAndPublication);
}
/// <summary>
/// Creates a new dependency engine.
/// </summary>
/// <typeparam name="TConfigurator">
/// The type of the object that configures containers.
/// </typeparam>
/// <typeparam name="TEngine">
/// The type of the dependency engine that is produced by the package.
/// </typeparam>
/// <typeparam name="TPackage">
/// The type of the package that configures the engine.
/// </typeparam>
/// <param name="applicationConfiguration">
/// Configuration information for the application.
/// </param>
/// <returns>
/// A new dependency engine.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="applicationConfiguration" /> is <see langword="null" />.
/// </exception>
/// <exception cref="ContainerConfigurationException">
/// An exception was raised while attempting to configure a container.
/// </exception>
public static TEngine New<TConfigurator, TEngine, TPackage>(IConfiguration applicationConfiguration)
where TConfigurator : class, new()
where TEngine : class, IDependencyEngine
where TPackage : class, IDependencyPackage<TConfigurator, TEngine>, new() => New<TConfigurator, TEngine, TPackage>(applicationConfiguration, EmptyServiceCollection);
/// <summary>
/// Creates a new dependency engine.
/// </summary>
/// <typeparam name="TConfigurator">
/// The type of the object that configures containers.
/// </typeparam>
/// <typeparam name="TEngine">
/// The type of the dependency engine that is produced by the package.
/// </typeparam>
/// <typeparam name="TPackage">
/// The type of the package that configures the engine.
/// </typeparam>
/// <param name="applicationConfiguration">
/// Configuration information for the application.
/// </param>
/// <param name="serviceDescriptors">
/// A collection of service descriptors to which the engine will supply dependencies.
/// </param>
/// <returns>
/// A new dependency engine.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="applicationConfiguration" /> is <see langword="null" /> -or- <paramref name="serviceDescriptors" /> is
/// <see langword="null" />.
/// </exception>
/// <exception cref="ContainerConfigurationException">
/// An exception was raised while attempting to configure a container.
/// </exception>
[DebuggerHidden]
internal static TEngine New<TConfigurator, TEngine, TPackage>(IConfiguration applicationConfiguration, IServiceCollection serviceDescriptors)
where TConfigurator : class, new()
where TEngine : class, IDependencyEngine
where TPackage : class, IDependencyPackage<TConfigurator, TEngine>, new()
{
var package = new TPackage();
var engine = package.CreateEngine(applicationConfiguration.RejectIf().IsNull(nameof(applicationConfiguration)).TargetArgument, serviceDescriptors);
var abstractEngine = engine as DependencyEngine;
if (abstractEngine is null == false)
{
abstractEngine.Start();
}
return engine;
}
/// <summary>
/// Initializes and configures all of the engine's components and enforces operational state.
/// </summary>
/// <exception cref="CreateDependencyScopeException">
/// An exception was raised while attempting to create a new scope.
/// </exception>
/// <exception cref="InvalidOperationException">
/// The engine is in a corrupt state.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// The engine and/or its container are disposed.
/// </exception>
[DebuggerHidden]
internal virtual void Start()
{
try
{
if (Container is null)
{
throw new InvalidOperationException("The dependency engine is in a corrupt state. The container is a null reference.");
}
using (var scope = Container.CreateScope())
{
if (scope is null)
{
throw new InvalidOperationException("The dependency engine is in a corrupt state. The container produced a null reference scope.");
}
}
}
catch (InvalidOperationException)
{
throw;
}
catch (Exception exception)
{
throw new InvalidOperationException("The dependency engine is in a corrupt state. See inner exception for details.", exception);
}
}
/// <summary>
/// Creates a new dependency container.
/// </summary>
/// <returns>
/// A new dependency container.
/// </returns>
protected abstract IDependencyContainer CreateContainer();
/// <summary>
/// Creates a new service provider.
/// </summary>
/// <returns>
/// A new service provider.
/// </returns>
protected virtual IServiceProvider CreateProvider() => new ServiceProvider(Container);
/// <summary>
/// Releases all resources consumed by the current <see cref="DependencyEngine" />.
/// </summary>
/// <param name="disposing">
/// A value indicating whether or not managed resources should be released.
/// </param>
protected override void Dispose(Boolean disposing)
{
try
{
if (disposing)
{
LazyContainer.Dispose();
}
}
finally
{
base.Dispose(disposing);
}
}
/// <summary>
/// Gets the engine's dependency container.
/// </summary>
/// <exception cref="ContainerConfigurationException">
/// An exception was raised while configuring the container.
/// </exception>
public IDependencyContainer Container => LazyContainer.Value;
/// <summary>
/// Gets the engine's service provider.
/// </summary>
/// <exception cref="ContainerConfigurationException">
/// An exception was raised while configuring the container.
/// </exception>
public IServiceProvider Provider => LazyProvider.Value;
/// <summary>
/// Represents a connection string value that instructs dependency modules to register in-memory connections.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal const String InMemoryConnectionStringValue = "InMemory";
/// <summary>
/// Represents an empty service descriptor collection.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal static readonly IServiceCollection EmptyServiceCollection = new ServiceCollection();
/// <summary>
/// Represents the engine's lazily-initialized dependency container.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Lazy<IDependencyContainer> LazyContainer;
/// <summary>
/// Represents the engine's lazily-initialized service provider.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Lazy<IServiceProvider> LazyProvider;
}
}