| | | 1 | | using Microsoft.Extensions.DependencyInjection; |
| | | 2 | | using TeleFlow.Framework.DependencyInjection; |
| | | 3 | | using TeleFlow.Framework.Dispatching; |
| | | 4 | | using TeleFlow.Framework.Middleware; |
| | | 5 | | using TeleFlow.Framework.Updates; |
| | | 6 | | |
| | | 7 | | namespace TeleFlow.Framework.Application; |
| | | 8 | | |
| | | 9 | | /// <summary> |
| | | 10 | | /// Composes the Core runtime pipeline from registered services: update source, update processor, |
| | | 11 | | /// dispatcher, middleware, and application lifecycle tasks. |
| | | 12 | | /// </summary> |
| | | 13 | | internal static class TeleFlowApplicationRuntimeFactory |
| | | 14 | | { |
| | | 15 | | public static ITeleFlowApplication CreateOwned(IServiceCollection services) |
| | | 16 | | { |
| | 54 | 17 | | ArgumentNullException.ThrowIfNull(services); |
| | | 18 | | |
| | 54 | 19 | | ValidateServiceDescriptors(services); |
| | | 20 | | |
| | 51 | 21 | | var serviceProvider = services.BuildServiceProvider(new ServiceProviderOptions |
| | 51 | 22 | | { |
| | 51 | 23 | | ValidateOnBuild = true, |
| | 51 | 24 | | ValidateScopes = true |
| | 51 | 25 | | }); |
| | | 26 | | |
| | | 27 | | try |
| | | 28 | | { |
| | 51 | 29 | | return Create(serviceProvider, ownsServices: true); |
| | | 30 | | } |
| | 6 | 31 | | catch |
| | | 32 | | { |
| | 6 | 33 | | serviceProvider.Dispose(); |
| | 6 | 34 | | throw; |
| | | 35 | | } |
| | 45 | 36 | | } |
| | | 37 | | |
| | | 38 | | public static ITeleFlowApplication CreateBorrowed( |
| | | 39 | | IServiceProvider serviceProvider, |
| | | 40 | | IEnumerable<ServiceDescriptor>? serviceDescriptors = null) |
| | | 41 | | { |
| | 4 | 42 | | ArgumentNullException.ThrowIfNull(serviceProvider); |
| | | 43 | | |
| | 4 | 44 | | if (serviceDescriptors is not null) |
| | | 45 | | { |
| | 4 | 46 | | ValidateServiceDescriptors(serviceDescriptors); |
| | | 47 | | } |
| | | 48 | | |
| | 3 | 49 | | return Create(serviceProvider, ownsServices: false); |
| | | 50 | | } |
| | | 51 | | |
| | | 52 | | private static ITeleFlowApplication Create(IServiceProvider serviceProvider, bool ownsServices) |
| | | 53 | | { |
| | 54 | 54 | | TeleFlowRuntimeValidatorRunner.Validate(serviceProvider); |
| | | 55 | | |
| | 50 | 56 | | var scopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>(); |
| | 50 | 57 | | var updateSource = ResolveSingleRequiredService<IUpdateSource>(serviceProvider); |
| | 49 | 58 | | var dispatcher = ResolveSingleRequiredService<IUpdateDispatcher>(serviceProvider); |
| | 48 | 59 | | var middleware = serviceProvider.GetServices<UpdateMiddlewareRegistration>().ToArray(); |
| | 48 | 60 | | var startupTasks = serviceProvider.GetServices<TeleFlowStartupTaskRegistration>().ToArray(); |
| | 48 | 61 | | var shutdownTasks = serviceProvider.GetServices<TeleFlowShutdownTaskRegistration>().ToArray(); |
| | 48 | 62 | | var processor = new DefaultUpdateProcessor(scopeFactory, dispatcher, middleware); |
| | 48 | 63 | | var lifecycle = new TeleFlowApplicationLifecycleRunner(scopeFactory, startupTasks, shutdownTasks); |
| | | 64 | | |
| | 48 | 65 | | return new TeleFlowApplication(serviceProvider, updateSource, processor, lifecycle, ownsServices); |
| | | 66 | | } |
| | | 67 | | |
| | | 68 | | private static TService ResolveSingleRequiredService<TService>(IServiceProvider serviceProvider) |
| | | 69 | | where TService : class |
| | | 70 | | { |
| | 99 | 71 | | var services = serviceProvider.GetServices<TService>().ToArray(); |
| | | 72 | | |
| | 99 | 73 | | return services.Length switch |
| | 99 | 74 | | { |
| | 2 | 75 | | 0 => throw new InvalidOperationException( |
| | 2 | 76 | | $"A single {typeof(TService).Name} registration is required to build the TeleFlow application."), |
| | 0 | 77 | | > 1 => throw new InvalidOperationException( |
| | 0 | 78 | | $"Only one {typeof(TService).Name} registration is supported when building the TeleFlow application."), |
| | 97 | 79 | | _ => services[0] |
| | 99 | 80 | | }; |
| | | 81 | | } |
| | | 82 | | |
| | | 83 | | private static void ValidateServiceDescriptors(IEnumerable<ServiceDescriptor> services) |
| | | 84 | | { |
| | 58 | 85 | | var descriptors = services as ICollection<ServiceDescriptor> ?? services.ToArray(); |
| | | 86 | | |
| | 1399 | 87 | | if (descriptors.Any(static descriptor => descriptor.ServiceType == typeof(IUpdateMiddleware))) |
| | | 88 | | { |
| | 2 | 89 | | throw new InvalidOperationException( |
| | 2 | 90 | | "Direct IUpdateMiddleware service registrations are not used by the TeleFlow update pipeline. " + |
| | 2 | 91 | | $"Register middleware with {nameof(ServiceCollectionMiddlewareExtensions.AddUpdateMiddleware)}<TMiddlewa |
| | 2 | 92 | | $"or {nameof(ServiceCollectionMiddlewareExtensions.AddSingletonUpdateMiddleware)}<TMiddleware>() so Tele |
| | 2 | 93 | | "resolve it from the correct update scope."); |
| | | 94 | | } |
| | | 95 | | |
| | 1384 | 96 | | if (descriptors.Any(static descriptor => descriptor.ServiceType == typeof(ITeleFlowStartupTask))) |
| | | 97 | | { |
| | 1 | 98 | | throw new InvalidOperationException( |
| | 1 | 99 | | "Direct ITeleFlowStartupTask service registrations are not used by the TeleFlow application lifecycle. " |
| | 1 | 100 | | $"Register startup tasks with {nameof(ApplicationLifecycleServiceCollectionExtensions.AddTeleFlowStartup |
| | 1 | 101 | | "so TeleFlow can resolve them from a dedicated lifecycle scope."); |
| | | 102 | | } |
| | | 103 | | |
| | 1375 | 104 | | if (descriptors.Any(static descriptor => descriptor.ServiceType == typeof(ITeleFlowShutdownTask))) |
| | | 105 | | { |
| | 1 | 106 | | throw new InvalidOperationException( |
| | 1 | 107 | | "Direct ITeleFlowShutdownTask service registrations are not used by the TeleFlow application lifecycle. |
| | 1 | 108 | | $"Register shutdown tasks with {nameof(ApplicationLifecycleServiceCollectionExtensions.AddTeleFlowShutdo |
| | 1 | 109 | | "so TeleFlow can resolve them from a dedicated lifecycle scope."); |
| | | 110 | | } |
| | 54 | 111 | | } |
| | | 112 | | } |