diff --git a/src/modules/Elsa.Modules.Activities/Resolvers/SwitchActivityNodeResolver.cs b/src/core/Elsa.Core/ActivityNodeResolvers/SwitchActivityNodeResolver.cs similarity index 93% rename from src/modules/Elsa.Modules.Activities/Resolvers/SwitchActivityNodeResolver.cs rename to src/core/Elsa.Core/ActivityNodeResolvers/SwitchActivityNodeResolver.cs index 8aa21232f..9a0f21a5c 100644 --- a/src/modules/Elsa.Modules.Activities/Resolvers/SwitchActivityNodeResolver.cs +++ b/src/core/Elsa.Core/ActivityNodeResolvers/SwitchActivityNodeResolver.cs @@ -1,7 +1,7 @@ using Elsa.Activities; using Elsa.Services; -namespace Elsa.Modules.Activities.Resolvers; +namespace Elsa.ActivityNodeResolvers; /// /// Returns a list of outbound activities for a given activity's branches. diff --git a/src/core/Elsa.Core/Models/ActivityExecutionContext.cs b/src/core/Elsa.Core/Models/ActivityExecutionContext.cs index d597e7f56..9c941b7d6 100644 --- a/src/core/Elsa.Core/Models/ActivityExecutionContext.cs +++ b/src/core/Elsa.Core/Models/ActivityExecutionContext.cs @@ -138,6 +138,10 @@ public class ActivityExecutionContext public T GetRequiredService() where T : notnull => WorkflowExecutionContext.GetRequiredService(); public object GetRequiredService(Type serviceType) => WorkflowExecutionContext.GetRequiredService(serviceType); + public T GetOrCreateService() where T : notnull => WorkflowExecutionContext.GetOrCreateService(); + public object GetOrCreateService(Type serviceType) => WorkflowExecutionContext.GetOrCreateService(serviceType); + public T? GetService() where T : notnull => WorkflowExecutionContext.GetService(); + public object? GetService(Type serviceType) => WorkflowExecutionContext.GetService(serviceType); public T? Get(Input? input) => input == null ? default : Get(input.LocationReference); public object? Get(RegisterLocationReference locationReference) diff --git a/src/core/Elsa.Core/Models/WorkflowExecutionContext.cs b/src/core/Elsa.Core/Models/WorkflowExecutionContext.cs index 1acb2f4f3..c8db29de5 100644 --- a/src/core/Elsa.Core/Models/WorkflowExecutionContext.cs +++ b/src/core/Elsa.Core/Models/WorkflowExecutionContext.cs @@ -82,6 +82,10 @@ public class WorkflowExecutionContext public T GetRequiredService() where T : notnull => _serviceProvider.GetRequiredService(); public object GetRequiredService(Type serviceType) => _serviceProvider.GetRequiredService(serviceType); + public T GetOrCreateService() where T : notnull => ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider); + public object GetOrCreateService(Type serviceType) => ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, serviceType); + public T? GetService() where T : notnull => _serviceProvider.GetService(); + public object? GetService(Type serviceType) => _serviceProvider.GetService(serviceType); public void AddCompletionCallback(ActivityExecutionContext owner, IActivity child, ActivityCompletionCallback completionCallback) { diff --git a/src/core/Elsa.Core/Options/ElsaOptionsConfigurator.cs b/src/core/Elsa.Core/Options/ElsaOptionsConfigurator.cs index a47086df3..dcad1e69a 100644 --- a/src/core/Elsa.Core/Options/ElsaOptionsConfigurator.cs +++ b/src/core/Elsa.Core/Options/ElsaOptionsConfigurator.cs @@ -51,8 +51,8 @@ public class ElsaOptionsConfigurator foreach (var configurator in _configurators) { - configurator.ConfigureServices(Services); - configurator.ConfigureHostedServices(Services); + configurator.ConfigureServices(this); + configurator.ConfigureHostedServices(this); } foreach (var hostedServiceDescriptor in _hostedServiceDescriptors.OrderBy(x => x.Order)) @@ -72,7 +72,6 @@ public class ElsaOptionsConfigurator .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -86,6 +85,10 @@ public class ElsaOptionsConfigurator // Pipelines. .AddSingleton() .AddSingleton() + + // Built-in activity services. + .AddSingleton() + .AddSingleton() // Logging .AddLogging(); diff --git a/src/core/Elsa.Core/Services/IConfigurator.cs b/src/core/Elsa.Core/Services/IConfigurator.cs index eecac9cd6..1313d271c 100644 --- a/src/core/Elsa.Core/Services/IConfigurator.cs +++ b/src/core/Elsa.Core/Services/IConfigurator.cs @@ -1,20 +1,20 @@ -using Microsoft.Extensions.DependencyInjection; +using Elsa.Options; namespace Elsa.Services; public interface IConfigurator { - void ConfigureServices(IServiceCollection services); - void ConfigureHostedServices(IServiceCollection services); + void ConfigureServices(ElsaOptionsConfigurator configurator); + void ConfigureHostedServices(ElsaOptionsConfigurator configurator); } public abstract class ConfiguratorBase : IConfigurator { - public virtual void ConfigureServices(IServiceCollection services) + public virtual void ConfigureServices(ElsaOptionsConfigurator configurator) { } - public virtual void ConfigureHostedServices(IServiceCollection services) + public virtual void ConfigureHostedServices(ElsaOptionsConfigurator configurator) { } } \ No newline at end of file diff --git a/src/core/Elsa.Persistence.Abstractions/Extensions/ServiceCollectionExtensions.cs b/src/core/Elsa.Persistence.Abstractions/Extensions/ServiceCollectionExtensions.cs index 81e43c07a..ff6db2586 100644 --- a/src/core/Elsa.Persistence.Abstractions/Extensions/ServiceCollectionExtensions.cs +++ b/src/core/Elsa.Persistence.Abstractions/Extensions/ServiceCollectionExtensions.cs @@ -5,7 +5,7 @@ namespace Elsa.Persistence.Extensions; public static class ServiceCollectionExtensions { - public static ElsaOptionsConfigurator UsePersistence(this ElsaOptionsConfigurator configurator, Action? configure = default) + public static ElsaOptionsConfigurator ConfigurePersistence(this ElsaOptionsConfigurator configurator, Action? configure = default) { configurator.Configure(() => new PersistenceOptions(configurator), configure); return configurator; diff --git a/src/core/Elsa.Persistence.Abstractions/Options/PersistenceOptions.cs b/src/core/Elsa.Persistence.Abstractions/Options/PersistenceOptions.cs index 046eff8ad..4770ca6dc 100644 --- a/src/core/Elsa.Persistence.Abstractions/Options/PersistenceOptions.cs +++ b/src/core/Elsa.Persistence.Abstractions/Options/PersistenceOptions.cs @@ -51,9 +51,9 @@ public class PersistenceOptions : ConfiguratorBase return this; } - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(ElsaOptionsConfigurator configurator) { - services + configurator.Services .AddSingleton(WorkflowDefinitionStore) .AddSingleton(WorkflowInstanceStore) .AddSingleton(WorkflowBookmarkStore) diff --git a/src/core/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs b/src/core/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs index 2eb3f8308..82717b63e 100644 --- a/src/core/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs +++ b/src/core/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs @@ -1,71 +1,20 @@ using System.Threading.Channels; -using Elsa.Expressions; -using Elsa.Mediator.Extensions; using Elsa.Options; -using Elsa.Runtime.HostedServices; -using Elsa.Runtime.Implementations; -using Elsa.Runtime.Interpreters; -using Elsa.Runtime.Models; using Elsa.Runtime.Options; using Elsa.Runtime.Services; -using Elsa.Runtime.Stimuli.Handlers; -using Elsa.Runtime.WorkflowProviders; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; namespace Elsa.Runtime.Extensions; public static class ServiceCollectionExtensions { - public static IServiceCollection AddElsaRuntime(this ElsaOptionsConfigurator configurator) + public static ElsaOptionsConfigurator ConfigureElsaRuntime(this ElsaOptionsConfigurator configurator, Action? configure = default) { - var services = configurator.Services; - services.AddOptions(); - - // Add ordered hosted services. - configurator - .AddHostedService() - .AddHostedService() - .AddHostedService() - .AddHostedService() - .AddHostedService(); - - return services - // Core. - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(sp => sp.GetRequiredService>().Value.WorkflowInvokerFactory(sp)) - .AddSingleton(sp => sp.GetRequiredService>().Value.WorkflowDispatcherFactory(sp)) - - // Stimulus handlers. - .AddStimulusHandler() - .AddStimulusHandler() - - // Instruction interpreters. - .AddInstructionInterpreter() - .AddInstructionInterpreter() - - // Workflow definition providers. - .AddWorkflowDefinitionProvider() - - // Workflow engine. - .AddSingleton() - - // Domain event handlers. - .AddNotificationHandlersFrom(typeof(ServiceCollectionExtensions)) - - // Channels for dispatching workflows in-memory. - .CreateChannel() - .CreateChannel() - ; + configurator.Configure(configure); + return configurator; } - private static IServiceCollection CreateChannel(this IServiceCollection services) => + public static IServiceCollection CreateChannel(this IServiceCollection services) => services .AddSingleton(CreateChannel()) .AddSingleton(CreateChannelReader) @@ -78,5 +27,5 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddWorkflowDefinitionProvider(this IServiceCollection services) where T : class, IWorkflowDefinitionProvider => services.AddSingleton(); public static IServiceCollection AddStimulusHandler(this IServiceCollection services) where T : class, IStimulusHandler => services.AddSingleton(); public static IServiceCollection AddInstructionInterpreter(this IServiceCollection services) where T : class, IWorkflowInstructionInterpreter => services.AddSingleton(); - public static IServiceCollection ConfigureWorkflowRuntime(this IServiceCollection services, Action configure) => services.Configure(configure); + public static IServiceCollection ConfigureWorkflowRuntime(this IServiceCollection services, Action configure) => services.Configure(configure); } \ No newline at end of file diff --git a/src/core/Elsa.Runtime/Options/ElsaRuntimeOptions.cs b/src/core/Elsa.Runtime/Options/ElsaRuntimeOptions.cs new file mode 100644 index 000000000..c9cc37674 --- /dev/null +++ b/src/core/Elsa.Runtime/Options/ElsaRuntimeOptions.cs @@ -0,0 +1,83 @@ +using Elsa.Mediator.Extensions; +using Elsa.Options; +using Elsa.Runtime.Extensions; +using Elsa.Runtime.HostedServices; +using Elsa.Runtime.Implementations; +using Elsa.Runtime.Interpreters; +using Elsa.Runtime.Models; +using Elsa.Runtime.Services; +using Elsa.Runtime.Stimuli.Handlers; +using Elsa.Runtime.WorkflowProviders; +using Elsa.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Elsa.Runtime.Options; + +public class ElsaRuntimeOptions : ConfiguratorBase +{ + /// + /// A list of workflow builders configured during application startup. + /// + public IDictionary>> Workflows { get; set; } = new Dictionary>>(); + + /// + /// A factory that instantiates a concrete . + /// + public Func WorkflowInvokerFactory { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates a concrete . + /// + public Func WorkflowDispatcherFactory { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + public override void ConfigureServices(ElsaOptionsConfigurator configurator) + { + var services = configurator.Services; + services.AddOptions(); + + services + // Core. + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService>().Value.WorkflowInvokerFactory(sp)) + .AddSingleton(sp => sp.GetRequiredService>().Value.WorkflowDispatcherFactory(sp)) + + // Stimulus handlers. + .AddStimulusHandler() + .AddStimulusHandler() + + // Instruction interpreters. + .AddInstructionInterpreter() + .AddInstructionInterpreter() + + // Workflow definition providers. + .AddWorkflowDefinitionProvider() + + // Workflow engine. + .AddSingleton() + + // Domain event handlers. + .AddNotificationHandlersFrom(typeof(ServiceCollectionExtensions)) + + // Channels for dispatching workflows in-memory. + .CreateChannel() + .CreateChannel() + ; + } + + public override void ConfigureHostedServices(ElsaOptionsConfigurator configurator) + { + configurator + .AddHostedService() + .AddHostedService() + .AddHostedService() + .AddHostedService() + .AddHostedService(); + } +} \ No newline at end of file diff --git a/src/core/Elsa.Runtime/Options/WorkflowRuntimeOptions.cs b/src/core/Elsa.Runtime/Options/WorkflowRuntimeOptions.cs deleted file mode 100644 index 25a891709..000000000 --- a/src/core/Elsa.Runtime/Options/WorkflowRuntimeOptions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Elsa.Runtime.Implementations; -using Elsa.Runtime.Services; -using Elsa.Services; -using Microsoft.Extensions.DependencyInjection; - -namespace Elsa.Runtime.Options; - -public class WorkflowRuntimeOptions -{ - /// - /// A list of workflow builders configured during application startup. - /// - public IDictionary>> Workflows { get; set; } = new Dictionary>>(); - - /// - /// A factory that instantiates a concrete . - /// - public Func WorkflowInvokerFactory { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); - - /// - /// A factory that instantiates a concrete . - /// - public Func WorkflowDispatcherFactory { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); -} \ No newline at end of file diff --git a/src/core/Elsa.Runtime/WorkflowProviders/ClrWorkflowDefinitionProvider.cs b/src/core/Elsa.Runtime/WorkflowProviders/ClrWorkflowDefinitionProvider.cs index 6e21aba7a..bb8039b0c 100644 --- a/src/core/Elsa.Runtime/WorkflowProviders/ClrWorkflowDefinitionProvider.cs +++ b/src/core/Elsa.Runtime/WorkflowProviders/ClrWorkflowDefinitionProvider.cs @@ -11,17 +11,17 @@ using Microsoft.Extensions.Options; namespace Elsa.Runtime.WorkflowProviders; /// -/// Provides workflows to the system that are registered with +/// Provides workflows to the system that are registered with /// public class ClrWorkflowDefinitionProvider : IWorkflowDefinitionProvider { private readonly IIdentityGraphService _identityGraphService; private readonly WorkflowSerializerOptionsProvider _workflowSerializerOptionsProvider; private readonly IServiceProvider _serviceProvider; - private readonly WorkflowRuntimeOptions _options; + private readonly ElsaRuntimeOptions _options; public ClrWorkflowDefinitionProvider( - IOptions options, + IOptions options, IIdentityGraphService identityGraphService, WorkflowSerializerOptionsProvider workflowSerializerOptionsProvider, IServiceProvider serviceProvider diff --git a/src/core/Elsa/Extensions/ServiceCollectionExtensions.cs b/src/core/Elsa/Extensions/ServiceCollectionExtensions.cs index 83640617f..25c783617 100644 --- a/src/core/Elsa/Extensions/ServiceCollectionExtensions.cs +++ b/src/core/Elsa/Extensions/ServiceCollectionExtensions.cs @@ -19,9 +19,9 @@ public static class ServiceCollectionExtensions return services .AddElsaCore(elsa => { - elsa.UsePersistence(persistence => persistence.UseInMemoryProvider()); - elsa.UseActivityServices(); - elsa.AddElsaRuntime(); + elsa.ConfigurePersistence(persistence => persistence.UseInMemoryProvider()); + elsa.ConfigureCoreActivityServices(); + elsa.ConfigureElsaRuntime(); elsa.AddElsaManagement(); configure?.Invoke(elsa); }); diff --git a/src/modules/Elsa.Modules.Activities/Activities/Console/ReadLine.cs b/src/modules/Elsa.Modules.Activities/Activities/Console/ReadLine.cs index 9e18edf78..781d733f1 100644 --- a/src/modules/Elsa.Modules.Activities/Activities/Console/ReadLine.cs +++ b/src/modules/Elsa.Modules.Activities/Activities/Console/ReadLine.cs @@ -1,5 +1,6 @@ using Elsa.Attributes; using Elsa.Models; +using Elsa.Modules.Activities.Providers; using Elsa.Modules.Activities.Services; namespace Elsa.Modules.Activities.Console; @@ -15,7 +16,7 @@ public class ReadLine : Activity protected override void Execute(ActivityExecutionContext context) { - var provider = context.GetRequiredService(); + var provider = context.GetService() ?? new StandardInStreamProvider(System.Console.In); var reader = provider.GetTextReader(); var text = reader.ReadLine(); context.Set(Result, text); diff --git a/src/modules/Elsa.Modules.Activities/Activities/Console/WriteLine.cs b/src/modules/Elsa.Modules.Activities/Activities/Console/WriteLine.cs index befc8c12d..e11c8aa5f 100644 --- a/src/modules/Elsa.Modules.Activities/Activities/Console/WriteLine.cs +++ b/src/modules/Elsa.Modules.Activities/Activities/Console/WriteLine.cs @@ -1,6 +1,7 @@ using System.ComponentModel; using Elsa.Attributes; using Elsa.Models; +using Elsa.Modules.Activities.Providers; using Elsa.Modules.Activities.Services; namespace Elsa.Modules.Activities.Console; @@ -35,7 +36,7 @@ public class WriteLine : Activity protected override void Execute(ActivityExecutionContext context) { var text = context.Get(Text); - var provider = context.GetRequiredService(); + var provider = context.GetService() ?? new StandardOutStreamProvider(System.Console.Out); var textWriter = provider.GetTextWriter(); textWriter.WriteLine(text); } diff --git a/src/modules/Elsa.Modules.Activities/Extensions/ServiceCollectionExtensions.cs b/src/modules/Elsa.Modules.Activities/Extensions/ServiceCollectionExtensions.cs index 7cdca6151..94be7adf1 100644 --- a/src/modules/Elsa.Modules.Activities/Extensions/ServiceCollectionExtensions.cs +++ b/src/modules/Elsa.Modules.Activities/Extensions/ServiceCollectionExtensions.cs @@ -8,7 +8,7 @@ public static class ServiceCollectionExtensions /// /// Registers required services for activities provided by this package. /// - public static ActivityOptions UseActivityServices(this ElsaOptionsConfigurator configurator) + public static ActivityOptions ConfigureCoreActivityServices(this ElsaOptionsConfigurator configurator) { return configurator.Configure(); } diff --git a/src/modules/Elsa.Modules.Activities/Options/ActivityOptions.cs b/src/modules/Elsa.Modules.Activities/Options/ActivityOptions.cs index a6404c381..4418f53a1 100644 --- a/src/modules/Elsa.Modules.Activities/Options/ActivityOptions.cs +++ b/src/modules/Elsa.Modules.Activities/Options/ActivityOptions.cs @@ -1,7 +1,7 @@ using Elsa.Modules.Activities.Configurators; using Elsa.Modules.Activities.Providers; -using Elsa.Modules.Activities.Resolvers; using Elsa.Modules.Activities.Services; +using Elsa.Options; using Elsa.Services; using Microsoft.Extensions.DependencyInjection; @@ -24,10 +24,9 @@ public class ActivityOptions : ConfiguratorBase return this; } - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(ElsaOptionsConfigurator configurator) { - services - .AddSingleton() + configurator.Services .AddSingleton() .AddSingleton(StandardInStreamProvider) .AddSingleton(StandardOutStreamProvider); diff --git a/src/persistence/efcore/Elsa.Persistence.EntityFrameworkCore/Options/EFCorePersistenceOptions.cs b/src/persistence/efcore/Elsa.Persistence.EntityFrameworkCore/Options/EFCorePersistenceOptions.cs index 97a627c24..4106b23b1 100644 --- a/src/persistence/efcore/Elsa.Persistence.EntityFrameworkCore/Options/EFCorePersistenceOptions.cs +++ b/src/persistence/efcore/Elsa.Persistence.EntityFrameworkCore/Options/EFCorePersistenceOptions.cs @@ -1,3 +1,4 @@ +using Elsa.Options; using Elsa.Persistence.Entities; using Elsa.Persistence.EntityFrameworkCore.Handlers; using Elsa.Persistence.EntityFrameworkCore.HostedServices; @@ -49,8 +50,10 @@ public class EFCorePersistenceOptions : IConfigurator return this; } - public void ConfigureServices(IServiceCollection services) + public void ConfigureServices(ElsaOptionsConfigurator configurator) { + var services = configurator.Services; + if (ContextPoolingIsEnabled) services.AddPooledDbContextFactory(DbContextOptionsBuilderAction); else @@ -73,9 +76,9 @@ public class EFCorePersistenceOptions : IConfigurator ; } - public void ConfigureHostedServices(IServiceCollection services) + public void ConfigureHostedServices(ElsaOptionsConfigurator configurator) { if (AutoRunMigrationsIsEnabled) - PersistenceOptions.ElsaOptionsConfigurator.AddHostedService(-1); // Migrations need to run before other hosted services that depend on DB access. + configurator.AddHostedService(-1); // Migrations need to run before other hosted services that depend on DB access. } } \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.Web1/Program.cs b/src/samples/aspnet/Elsa.Samples.Web1/Program.cs index f87ed4cc1..44a5f30fb 100644 --- a/src/samples/aspnet/Elsa.Samples.Web1/Program.cs +++ b/src/samples/aspnet/Elsa.Samples.Web1/Program.cs @@ -40,7 +40,7 @@ var sqlServerConnectionString = configuration.GetConnectionString("SqlServer"); // Add services. services - .AddElsa(elsa => elsa.UsePersistence(p => p.UseEntityFrameworkCoreProvider(ef => ef.UseSqlite()))) + .AddElsa(elsa => elsa.ConfigurePersistence(p => p.UseEntityFrameworkCoreProvider(ef => ef.UseSqlite()))) //.AddProtoActorWorkflowHost() .AddJobServices(new QuartzJobSchedulerProvider(), new HangfireJobQueueProvider()) .AddSchedulingServices()