using System.Reflection; using Elsa.Features.Attributes; using Elsa.Features.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; namespace Elsa.Features.Implementations; public class Module : IModule { private record HostedServiceDescriptor(int Order, Type HostedServiceType); private readonly ISet _features = new HashSet(); private readonly ICollection _hostedServiceDescriptors = new List(); public Module(IServiceCollection services) { Services = services; } public IServiceCollection Services { get; } public T Configure(Action? configure = default) where T : class, IFeature => Configure(serviceConfiguration => (T)Activator.CreateInstance(typeof(T), serviceConfiguration)!, configure); public T Configure(Func factory, Action? configure = default) where T : class, IFeature { if (_features.FirstOrDefault(x => x is T) is not T configurator) { configurator = factory(this); _features.Add(configurator); } configure?.Invoke(configurator); return configurator; } public IModule ConfigureHostedService(int priority = 0) { _hostedServiceDescriptors.Add(new HostedServiceDescriptor(priority, typeof(T))); return this; } public void Apply() { ResolveDependencies(); foreach (var feature in _features) { feature.Configure(); feature.ConfigureHostedServices(); } foreach (var hostedServiceDescriptor in _hostedServiceDescriptors.OrderBy(x => x.Order)) Services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(IHostedService), hostedServiceDescriptor.HostedServiceType)); foreach (var feature in _features) { feature.Apply(); } } private void ResolveDependencies() { var resolvedDependencyTypes = new HashSet(); foreach (var configurator in _features.ToList()) ResolveDependencies(configurator, resolvedDependencyTypes); } private void ResolveDependencies(IFeature feature, ISet resolvedDependencyTypes) { var dependencyTypes = feature.GetType().GetCustomAttributes().Select(x => x.Type).ToHashSet(); dependencyTypes = dependencyTypes.Except(resolvedDependencyTypes).ToHashSet(); foreach (var type in dependencyTypes) { var dependencyConfigurator = AddConfigurator(type); resolvedDependencyTypes.Add(type); ResolveDependencies(dependencyConfigurator, resolvedDependencyTypes); } } private IFeature AddConfigurator(Type type) { var configurator = _features.FirstOrDefault(x => x.GetType() == type); if (configurator != null) return configurator; configurator = (IFeature)Activator.CreateInstance(type, this)!; _features.Add(configurator); return configurator; } }