From 7e8128e978a678b1f92275fcf523c2f54de91e2d Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 4 Dec 2025 13:30:41 +0100 Subject: [PATCH 01/36] Add CShells package references and integrate shell features into the application --- Directory.Packages.props | 2 + .../Elsa.Server.Web/Elsa.Server.Web.csproj | 4 + .../Features/ActivitiesAndWorkflowsFeature.cs | 16 +++ .../Features/TempElsaFeature.cs | 103 ++++++++++++++++++ src/apps/Elsa.Server.Web/Program.cs | 82 +------------- 5 files changed, 129 insertions(+), 78 deletions(-) create mode 100644 src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs create mode 100644 src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 534a30ab3d..a0dc39f0af 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -100,6 +100,8 @@ + + diff --git a/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj b/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj index 206866f3d3..40f9e4bc62 100644 --- a/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj +++ b/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj @@ -22,6 +22,10 @@ + + + + diff --git a/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs b/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs new file mode 100644 index 0000000000..579ad3dc91 --- /dev/null +++ b/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs @@ -0,0 +1,16 @@ +using CShells.Features; +using Elsa.Extensions; + +namespace Elsa.Server.Web.Features; + +public class ActivitiesAndWorkflowsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.ConfigureElsa(elsa => + { + elsa.AddActivitiesFrom(); + elsa.AddWorkflowsFrom(); + }); + } +} \ No newline at end of file diff --git a/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs b/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs new file mode 100644 index 0000000000..890aecbde7 --- /dev/null +++ b/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs @@ -0,0 +1,103 @@ +using System.Text.Encodings.Web; +using CShells.Features; +using Elsa.Extensions; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using Elsa.Persistence.EFCore.Modules.Runtime; +using Elsa.WorkflowProviders.BlobStorage.ElsaScript.Extensions; +using Elsa.Workflows.CommitStates.Strategies; +using Elsa.Workflows.LogPersistence; +using Elsa.Workflows.Runtime.Distributed.Extensions; + +namespace Elsa.Server.Web.Features; + +public class TempElsaFeature(IConfiguration configuration) : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + const bool useReadOnlyMode = false; + const bool useSignalR = false; // Disabled until Elsa Studio sends authenticated requests. + const bool useMultitenancy = false; + const bool disableVariableWrappers = false; + var identitySection = configuration.GetSection("Identity"); + var identityTokenSection = identitySection.GetSection("Tokens"); + + services + .AddElsa(elsa => + { + elsa + .AddActivitiesFrom() + .AddWorkflowsFrom() + .UseIdentity(identity => + { + identity.TokenOptions += options => identityTokenSection.Bind(options); + identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options)); + identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options)); + identity.UseConfigurationBasedRoleProvider(options => identitySection.Bind(options)); + }) + .UseDefaultAuthentication() + .UseWorkflows(workflows => + { + workflows.UseCommitStrategies(strategies => + { + strategies.AddStandardStrategies(); + strategies.Add("Every 10 seconds", new PeriodicWorkflowStrategy(TimeSpan.FromSeconds(10))); + }); + }) + .UseWorkflowManagement(management => + { + management.UseEntityFrameworkCore(ef => ef.UseSqlite()); + management.SetDefaultLogPersistenceMode(LogPersistenceMode.Inherit); + management.UseCache(); + management.UseReadOnlyMode(useReadOnlyMode); + }) + .UseWorkflowRuntime(runtime => + { + runtime.UseEntityFrameworkCore(ef => ef.UseSqlite()); + runtime.UseCache(); + runtime.UseDistributedRuntime(); + }) + .UseWorkflowsApi() + .UseFluentStorageProvider() + .UseElsaScriptBlobStorage() + .UseScheduling() + .UseCSharp(options => + { + options.DisableWrappers = disableVariableWrappers; + options.AppendScript("string Greet(string name) => $\"Hello {name}!\";"); + options.AppendScript("string SayHelloWorld() => Greet(\"World\");"); + }) + .UseJavaScript(options => + { + options.AllowClrAccess = true; + options.ConfigureEngine(engine => + { + engine.Execute("function greet(name) { return `Hello ${name}!`; }"); + engine.Execute("function sayHelloWorld() { return greet('World'); }"); + }); + }) + .UsePython(python => + { + python.PythonOptions += options => + { + // Make sure to configure the path to the python DLL. E.g. /opt/homebrew/Cellar/python@3.11/3.11.6_1/Frameworks/Python.framework/Versions/3.11/bin/python3.11 + // alternatively, you can set the PYTHONNET_PYDLL environment variable. + configuration.GetSection("Scripting:Python").Bind(options); + + options.AddScript(sb => + { + sb.AppendLine("def greet():"); + sb.AppendLine(" return \"Hello, welcome to Python!\""); + }); + }; + }) + .UseLiquid(liquid => liquid.FluidOptions = options => options.Encoder = HtmlEncoder.Default) + .UseHttp(http => + { + http.ConfigureHttpOptions = options => configuration.GetSection("Http").Bind(options); + http.UseCache(); + }); + Program.ConfigureForTest?.Invoke(elsa); + }); + } +} \ No newline at end of file diff --git a/src/apps/Elsa.Server.Web/Program.cs b/src/apps/Elsa.Server.Web/Program.cs index b056f50104..10ab7d100e 100644 --- a/src/apps/Elsa.Server.Web/Program.cs +++ b/src/apps/Elsa.Server.Web/Program.cs @@ -1,4 +1,6 @@ using System.Text.Encodings.Web; +using CShells.AspNetCore.Extensions; +using CShells.DependencyInjection; using Elsa.Caching.Options; using Elsa.Common.RecurringTasks; using Elsa.Expressions.Helpers; @@ -37,84 +39,8 @@ var identitySection = configuration.GetSection("Identity"); var identityTokenSection = identitySection.GetSection("Tokens"); -// Add Elsa services. -services - .AddElsa(elsa => - { - elsa - .AddActivitiesFrom() - .AddWorkflowsFrom() - .UseIdentity(identity => - { - identity.TokenOptions += options => identityTokenSection.Bind(options); - identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options)); - identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options)); - identity.UseConfigurationBasedRoleProvider(options => identitySection.Bind(options)); - }) - .UseDefaultAuthentication() - .UseWorkflows(workflows => - { - workflows.UseCommitStrategies(strategies => - { - strategies.AddStandardStrategies(); - strategies.Add("Every 10 seconds", new PeriodicWorkflowStrategy(TimeSpan.FromSeconds(10))); - }); - }) - .UseWorkflowManagement(management => - { - management.UseEntityFrameworkCore(ef => ef.UseSqlite()); - management.SetDefaultLogPersistenceMode(LogPersistenceMode.Inherit); - management.UseCache(); - management.UseReadOnlyMode(useReadOnlyMode); - }) - .UseWorkflowRuntime(runtime => - { - runtime.UseEntityFrameworkCore(ef => ef.UseSqlite()); - runtime.UseCache(); - runtime.UseDistributedRuntime(); - }) - .UseWorkflowsApi() - .UseFluentStorageProvider() - .UseElsaScriptBlobStorage() - .UseScheduling() - .UseCSharp(options => - { - options.DisableWrappers = disableVariableWrappers; - options.AppendScript("string Greet(string name) => $\"Hello {name}!\";"); - options.AppendScript("string SayHelloWorld() => Greet(\"World\");"); - }) - .UseJavaScript(options => - { - options.AllowClrAccess = true; - options.ConfigureEngine(engine => - { - engine.Execute("function greet(name) { return `Hello ${name}!`; }"); - engine.Execute("function sayHelloWorld() { return greet('World'); }"); - }); - }) - .UsePython(python => - { - python.PythonOptions += options => - { - // Make sure to configure the path to the python DLL. E.g. /opt/homebrew/Cellar/python@3.11/3.11.6_1/Frameworks/Python.framework/Versions/3.11/bin/python3.11 - // alternatively, you can set the PYTHONNET_PYDLL environment variable. - configuration.GetSection("Scripting:Python").Bind(options); - - options.AddScript(sb => - { - sb.AppendLine("def greet():"); - sb.AppendLine(" return \"Hello, welcome to Python!\""); - }); - }; - }) - .UseLiquid(liquid => liquid.FluidOptions = options => options.Encoder = HtmlEncoder.Default) - .UseHttp(http => - { - http.ConfigureHttpOptions = options => configuration.GetSection("Http").Bind(options); - http.UseCache(); - }); - ConfigureForTest?.Invoke(elsa); - }); +builder.AddShells(); + // Obfuscate HTTP request headers. services.AddActivityStateFilter(); From e8a875e8f9d14891c5403df50e8ac7e151db25dd Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 4 Dec 2025 21:13:51 +0100 Subject: [PATCH 02/36] Annotate shell features with `[ShellFeature]` attribute and update `TempElsaFeature` to use `ConfigureElsa`. --- .../Features/ActivitiesAndWorkflowsFeature.cs | 1 + src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs b/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs index 579ad3dc91..e907e78ceb 100644 --- a/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs +++ b/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs @@ -3,6 +3,7 @@ namespace Elsa.Server.Web.Features; +[ShellFeature("ActivitiesAndWorkflows", DependsOn = ["Elsa"])] public class ActivitiesAndWorkflowsFeature : IShellFeature { public void ConfigureServices(IServiceCollection services) diff --git a/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs b/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs index 890aecbde7..04aa919264 100644 --- a/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs +++ b/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs @@ -11,6 +11,7 @@ namespace Elsa.Server.Web.Features; +[ShellFeature("Elsa")] public class TempElsaFeature(IConfiguration configuration) : IShellFeature { public void ConfigureServices(IServiceCollection services) @@ -23,11 +24,9 @@ public void ConfigureServices(IServiceCollection services) var identityTokenSection = identitySection.GetSection("Tokens"); services - .AddElsa(elsa => + .ConfigureElsa(elsa => { elsa - .AddActivitiesFrom() - .AddWorkflowsFrom() .UseIdentity(identity => { identity.TokenOptions += options => identityTokenSection.Bind(options); From 096202d913568e22b8f1c0ebe211c909754768b6 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 5 Dec 2025 14:19:19 +0100 Subject: [PATCH 03/36] Revert "Annotate shell features with `[ShellFeature]` attribute and update `TempElsaFeature` to use `ConfigureElsa`." This reverts commit e8a875e8f9d14891c5403df50e8ac7e151db25dd. --- .../Features/ActivitiesAndWorkflowsFeature.cs | 1 - src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs b/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs index e907e78ceb..579ad3dc91 100644 --- a/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs +++ b/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs @@ -3,7 +3,6 @@ namespace Elsa.Server.Web.Features; -[ShellFeature("ActivitiesAndWorkflows", DependsOn = ["Elsa"])] public class ActivitiesAndWorkflowsFeature : IShellFeature { public void ConfigureServices(IServiceCollection services) diff --git a/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs b/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs index 04aa919264..890aecbde7 100644 --- a/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs +++ b/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs @@ -11,7 +11,6 @@ namespace Elsa.Server.Web.Features; -[ShellFeature("Elsa")] public class TempElsaFeature(IConfiguration configuration) : IShellFeature { public void ConfigureServices(IServiceCollection services) @@ -24,9 +23,11 @@ public void ConfigureServices(IServiceCollection services) var identityTokenSection = identitySection.GetSection("Tokens"); services - .ConfigureElsa(elsa => + .AddElsa(elsa => { elsa + .AddActivitiesFrom() + .AddWorkflowsFrom() .UseIdentity(identity => { identity.TokenOptions += options => identityTokenSection.Bind(options); From ff4342ab033c18eb6dd3d322981e26292313fbc8 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 5 Dec 2025 14:24:49 +0100 Subject: [PATCH 04/36] Introduce `Elsa.ModularServer.Web` with a minimal API, restructure shell feature configuration, and remove obsolete `CShells` dependency --- Elsa.sln | 7 ++ .../Elsa.ModularServer.Web.csproj | 9 ++ src/apps/Elsa.ModularServer.Web/Program.cs | 6 + .../Properties/launchSettings.json | 23 ++++ .../appsettings.Development.json | 8 ++ .../Elsa.ModularServer.Web/appsettings.json | 9 ++ .../Elsa.Server.Web/Elsa.Server.Web.csproj | 4 - .../Features/ActivitiesAndWorkflowsFeature.cs | 16 --- .../Features/TempElsaFeature.cs | 103 ------------------ src/apps/Elsa.Server.Web/Program.cs | 79 +++++++++++++- 10 files changed, 139 insertions(+), 125 deletions(-) create mode 100644 src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj create mode 100644 src/apps/Elsa.ModularServer.Web/Program.cs create mode 100644 src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json create mode 100644 src/apps/Elsa.ModularServer.Web/appsettings.Development.json create mode 100644 src/apps/Elsa.ModularServer.Web/appsettings.json delete mode 100644 src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs delete mode 100644 src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs diff --git a/Elsa.sln b/Elsa.sln index f8c579aff1..e2157d0f6b 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -337,6 +337,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issue_templates", "issue_te EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dsl", "dsl", "{477C2416-312D-46AE-BCD6-8FA1FAB43624}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.ModularServer.Web", "src\apps\Elsa.ModularServer.Web\Elsa.ModularServer.Web.csproj", "{E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -593,6 +595,10 @@ Global {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48}.Debug|Any CPU.Build.0 = Debug|Any CPU {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48}.Release|Any CPU.Build.0 = Release|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -695,6 +701,7 @@ Global {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48} = {B08B4E00-C2AB-48F3-8389-449F42AEF179} {477C2416-312D-46AE-BCD6-8FA1FAB43624} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79} {874F5A44-DB06-47AB-A18C-2D13942E0147} = {477C2416-312D-46AE-BCD6-8FA1FAB43624} + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC} = {D92BEAB2-60D6-4BB4-885A-6BA681C6CCF1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E} diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj new file mode 100644 index 0000000000..3cb3a892f2 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/src/apps/Elsa.ModularServer.Web/Program.cs b/src/apps/Elsa.ModularServer.Web/Program.cs new file mode 100644 index 0000000000..ec8e4f41d4 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/Program.cs @@ -0,0 +1,6 @@ +var builder = WebApplication.CreateBuilder(args); +var app = builder.Build(); + +app.MapGet("/", () => "Hello World!"); + +app.Run(); \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json b/src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json new file mode 100644 index 0000000000..49af15d915 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7293;http://localhost:5002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.Development.json b/src/apps/Elsa.ModularServer.Web/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj b/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj index 40f9e4bc62..206866f3d3 100644 --- a/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj +++ b/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj @@ -22,10 +22,6 @@ - - - - diff --git a/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs b/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs deleted file mode 100644 index 579ad3dc91..0000000000 --- a/src/apps/Elsa.Server.Web/Features/ActivitiesAndWorkflowsFeature.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CShells.Features; -using Elsa.Extensions; - -namespace Elsa.Server.Web.Features; - -public class ActivitiesAndWorkflowsFeature : IShellFeature -{ - public void ConfigureServices(IServiceCollection services) - { - services.ConfigureElsa(elsa => - { - elsa.AddActivitiesFrom(); - elsa.AddWorkflowsFrom(); - }); - } -} \ No newline at end of file diff --git a/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs b/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs deleted file mode 100644 index 890aecbde7..0000000000 --- a/src/apps/Elsa.Server.Web/Features/TempElsaFeature.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Text.Encodings.Web; -using CShells.Features; -using Elsa.Extensions; -using Elsa.Persistence.EFCore.Extensions; -using Elsa.Persistence.EFCore.Modules.Management; -using Elsa.Persistence.EFCore.Modules.Runtime; -using Elsa.WorkflowProviders.BlobStorage.ElsaScript.Extensions; -using Elsa.Workflows.CommitStates.Strategies; -using Elsa.Workflows.LogPersistence; -using Elsa.Workflows.Runtime.Distributed.Extensions; - -namespace Elsa.Server.Web.Features; - -public class TempElsaFeature(IConfiguration configuration) : IShellFeature -{ - public void ConfigureServices(IServiceCollection services) - { - const bool useReadOnlyMode = false; - const bool useSignalR = false; // Disabled until Elsa Studio sends authenticated requests. - const bool useMultitenancy = false; - const bool disableVariableWrappers = false; - var identitySection = configuration.GetSection("Identity"); - var identityTokenSection = identitySection.GetSection("Tokens"); - - services - .AddElsa(elsa => - { - elsa - .AddActivitiesFrom() - .AddWorkflowsFrom() - .UseIdentity(identity => - { - identity.TokenOptions += options => identityTokenSection.Bind(options); - identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options)); - identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options)); - identity.UseConfigurationBasedRoleProvider(options => identitySection.Bind(options)); - }) - .UseDefaultAuthentication() - .UseWorkflows(workflows => - { - workflows.UseCommitStrategies(strategies => - { - strategies.AddStandardStrategies(); - strategies.Add("Every 10 seconds", new PeriodicWorkflowStrategy(TimeSpan.FromSeconds(10))); - }); - }) - .UseWorkflowManagement(management => - { - management.UseEntityFrameworkCore(ef => ef.UseSqlite()); - management.SetDefaultLogPersistenceMode(LogPersistenceMode.Inherit); - management.UseCache(); - management.UseReadOnlyMode(useReadOnlyMode); - }) - .UseWorkflowRuntime(runtime => - { - runtime.UseEntityFrameworkCore(ef => ef.UseSqlite()); - runtime.UseCache(); - runtime.UseDistributedRuntime(); - }) - .UseWorkflowsApi() - .UseFluentStorageProvider() - .UseElsaScriptBlobStorage() - .UseScheduling() - .UseCSharp(options => - { - options.DisableWrappers = disableVariableWrappers; - options.AppendScript("string Greet(string name) => $\"Hello {name}!\";"); - options.AppendScript("string SayHelloWorld() => Greet(\"World\");"); - }) - .UseJavaScript(options => - { - options.AllowClrAccess = true; - options.ConfigureEngine(engine => - { - engine.Execute("function greet(name) { return `Hello ${name}!`; }"); - engine.Execute("function sayHelloWorld() { return greet('World'); }"); - }); - }) - .UsePython(python => - { - python.PythonOptions += options => - { - // Make sure to configure the path to the python DLL. E.g. /opt/homebrew/Cellar/python@3.11/3.11.6_1/Frameworks/Python.framework/Versions/3.11/bin/python3.11 - // alternatively, you can set the PYTHONNET_PYDLL environment variable. - configuration.GetSection("Scripting:Python").Bind(options); - - options.AddScript(sb => - { - sb.AppendLine("def greet():"); - sb.AppendLine(" return \"Hello, welcome to Python!\""); - }); - }; - }) - .UseLiquid(liquid => liquid.FluidOptions = options => options.Encoder = HtmlEncoder.Default) - .UseHttp(http => - { - http.ConfigureHttpOptions = options => configuration.GetSection("Http").Bind(options); - http.UseCache(); - }); - Program.ConfigureForTest?.Invoke(elsa); - }); - } -} \ No newline at end of file diff --git a/src/apps/Elsa.Server.Web/Program.cs b/src/apps/Elsa.Server.Web/Program.cs index 10ab7d100e..f0e3588690 100644 --- a/src/apps/Elsa.Server.Web/Program.cs +++ b/src/apps/Elsa.Server.Web/Program.cs @@ -39,8 +39,83 @@ var identitySection = configuration.GetSection("Identity"); var identityTokenSection = identitySection.GetSection("Tokens"); -builder.AddShells(); - +services + .AddElsa(elsa => + { + elsa + .AddActivitiesFrom() + .AddWorkflowsFrom() + .UseIdentity(identity => + { + identity.TokenOptions += options => identityTokenSection.Bind(options); + identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options)); + identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options)); + identity.UseConfigurationBasedRoleProvider(options => identitySection.Bind(options)); + }) + .UseDefaultAuthentication() + .UseWorkflows(workflows => + { + workflows.UseCommitStrategies(strategies => + { + strategies.AddStandardStrategies(); + strategies.Add("Every 10 seconds", new PeriodicWorkflowStrategy(TimeSpan.FromSeconds(10))); + }); + }) + .UseWorkflowManagement(management => + { + management.UseEntityFrameworkCore(ef => ef.UseSqlite()); + management.SetDefaultLogPersistenceMode(LogPersistenceMode.Inherit); + management.UseCache(); + management.UseReadOnlyMode(useReadOnlyMode); + }) + .UseWorkflowRuntime(runtime => + { + runtime.UseEntityFrameworkCore(ef => ef.UseSqlite()); + runtime.UseCache(); + runtime.UseDistributedRuntime(); + }) + .UseWorkflowsApi() + .UseFluentStorageProvider() + .UseElsaScriptBlobStorage() + .UseScheduling() + .UseCSharp(options => + { + options.DisableWrappers = disableVariableWrappers; + options.AppendScript("string Greet(string name) => $\"Hello {name}!\";"); + options.AppendScript("string SayHelloWorld() => Greet(\"World\");"); + }) + .UseJavaScript(options => + { + options.AllowClrAccess = true; + options.ConfigureEngine(engine => + { + engine.Execute("function greet(name) { return `Hello ${name}!`; }"); + engine.Execute("function sayHelloWorld() { return greet('World'); }"); + }); + }) + .UsePython(python => + { + python.PythonOptions += options => + { + // Make sure to configure the path to the python DLL. E.g. /opt/homebrew/Cellar/python@3.11/3.11.6_1/Frameworks/Python.framework/Versions/3.11/bin/python3.11 + // alternatively, you can set the PYTHONNET_PYDLL environment variable. + configuration.GetSection("Scripting:Python").Bind(options); + + options.AddScript(sb => + { + sb.AppendLine("def greet():"); + sb.AppendLine(" return \"Hello, welcome to Python!\""); + }); + }; + }) + .UseLiquid(liquid => liquid.FluidOptions = options => options.Encoder = HtmlEncoder.Default) + .UseHttp(http => + { + http.ConfigureHttpOptions = options => configuration.GetSection("Http").Bind(options); + http.UseCache(); + }); + ConfigureForTest?.Invoke(elsa); + }); // Obfuscate HTTP request headers. services.AddActivityStateFilter(); From 23a33c7634d01aa8090bd8279417063f320fa09e Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 6 Dec 2025 11:25:41 +0100 Subject: [PATCH 05/36] Update `CShells` package references, add Fody weaver, and configure new CShells package sources in `NuGet.Config`. --- Directory.Packages.props | 10 ++++++---- NuGet.Config | 5 +++++ .../Elsa.ModularServer.Web.csproj | 10 +++------- src/apps/Elsa.ModularServer.Web/FodyWeavers.xml | 3 +++ src/apps/Elsa.Server.Web/Program.cs | 2 -- .../Elsa.Workflows.Core/Elsa.Workflows.Core.csproj | 1 + 6 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 src/apps/Elsa.ModularServer.Web/FodyWeavers.xml diff --git a/Directory.Packages.props b/Directory.Packages.props index a0dc39f0af..440b2b0529 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -33,13 +33,13 @@ - + - + @@ -100,8 +100,10 @@ - - + + + + diff --git a/NuGet.Config b/NuGet.Config index 31400bd46d..f2e4972045 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -6,6 +6,7 @@ + @@ -20,5 +21,9 @@ + + + + \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj index 3cb3a892f2..a98c7781cd 100644 --- a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -1,9 +1,5 @@ - - - net10.0 - enable - enable - - + + + diff --git a/src/apps/Elsa.ModularServer.Web/FodyWeavers.xml b/src/apps/Elsa.ModularServer.Web/FodyWeavers.xml new file mode 100644 index 0000000000..00e1d9a1c1 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/apps/Elsa.Server.Web/Program.cs b/src/apps/Elsa.Server.Web/Program.cs index f0e3588690..6536aeac45 100644 --- a/src/apps/Elsa.Server.Web/Program.cs +++ b/src/apps/Elsa.Server.Web/Program.cs @@ -1,6 +1,4 @@ using System.Text.Encodings.Web; -using CShells.AspNetCore.Extensions; -using CShells.DependencyInjection; using Elsa.Caching.Options; using Elsa.Common.RecurringTasks; using Elsa.Expressions.Helpers; diff --git a/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj b/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj index e400062f4d..c24e99f046 100644 --- a/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj +++ b/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj @@ -12,6 +12,7 @@ + From 1ba3a5d6d3ccdd9117e016488541c96a593bf7e2 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 19 Dec 2025 14:34:38 +0100 Subject: [PATCH 06/36] Adds CShells integration to Elsa Integrates CShells to enhance modularity and extensibility. - Adds CShells related projects to the solution. - Updates NuGet configuration to include CShells preview feed. - Creates initial app settings for CShells configuration. - Adds CShells.AspNetCore project reference. - Implements CShells extensions in the program file. - Creates shell feature classes in Elsa.Common. - Creates shell feature classes in Elsa.Expressions. - Creates shell feature classes in Elsa.Workflows.Core. - Creates shell feature classes in Elsa.Workflows.Management. - Creates shell feature classes in Elsa.Workflows.Runtime. - Creates shell feature classes in Elsa module. --- Elsa.sln | 37 +++ NuGet.Config | 5 + .../Elsa.ModularServer.Web.csproj | 6 +- src/apps/Elsa.ModularServer.Web/Program.cs | 11 +- .../Elsa.ModularServer.Web/appsettings.json | 13 +- src/common/Elsa.Features/Elsa.Features.csproj | 5 + src/modules/Elsa.Common/Elsa.Common.csproj | 1 + .../DependencyInjectionExtensions.cs | 75 ++--- .../ShellFeatures/DefaultFormattersFeature.cs | 16 ++ .../ShellFeatures/MediatorFeature.cs | 17 ++ .../ShellFeatures/MultitenancyFeature.cs | 44 +++ .../ShellFeatures/StringCompressionFeature.cs | 20 ++ .../ShellFeatures/SystemClockFeature.cs | 16 ++ .../ShellFeatures/ExpressionsFeature.cs | 19 ++ .../Elsa.Workflows.Core.csproj | 2 +- .../ShellFeatures/CommitStrategiesFeature.cs | 16 ++ .../ShellFeatures/WorkflowsFeature.cs | 183 ++++++++++++ .../WorkflowManagementFeature.cs | 132 +++++++++ .../ShellFeatures/WorkflowRuntimeFeature.cs | 271 ++++++++++++++++++ src/modules/Elsa/Elsa.csproj | 14 +- src/modules/Elsa/ShellFeatures/ElsaFeature.cs | 17 ++ 21 files changed, 872 insertions(+), 48 deletions(-) create mode 100644 src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs create mode 100644 src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs create mode 100644 src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs create mode 100644 src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs create mode 100644 src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs create mode 100644 src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs create mode 100644 src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs create mode 100644 src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs create mode 100644 src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs create mode 100644 src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs create mode 100644 src/modules/Elsa/ShellFeatures/ElsaFeature.cs diff --git a/Elsa.sln b/Elsa.sln index e2157d0f6b..ccce177eba 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -339,6 +339,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dsl", "dsl", "{477C2416-312 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.ModularServer.Web", "src\apps\Elsa.ModularServer.Web\Elsa.ModularServer.Web.csproj", "{E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cshells", "cshells", "{71595D88-A05C-446E-B294-5CB6D91FB91F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells", "..\..\..\sfmskywalker\cshells\main\src\CShells\CShells.csproj", "{A70C589D-A498-4139-9CA9-CA82CB7864F0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.Abstractions", "..\..\..\sfmskywalker\cshells\main\src\CShells.Abstractions\CShells.Abstractions.csproj", "{E06268B5-C087-4508-86A7-8D8482759A0D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.AspNetCore.Abstractions", "..\..\..\sfmskywalker\cshells\main\src\CShells.AspNetCore.Abstractions\CShells.AspNetCore.Abstractions.csproj", "{4096D2AB-1C9D-40B9-9F5C-7235089759A5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.Providers.FluentStorage", "..\..\..\sfmskywalker\cshells\main\src\CShells.Providers.FluentStorage\CShells.Providers.FluentStorage.csproj", "{E8397C32-5BD2-4DE8-BB29-96624A9C75A0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.AspNetCore", "..\..\..\sfmskywalker\cshells\main\src\CShells.AspNetCore\CShells.AspNetCore.csproj", "{8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -599,6 +611,26 @@ Global {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Release|Any CPU.Build.0 = Release|Any CPU + {A70C589D-A498-4139-9CA9-CA82CB7864F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A70C589D-A498-4139-9CA9-CA82CB7864F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A70C589D-A498-4139-9CA9-CA82CB7864F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A70C589D-A498-4139-9CA9-CA82CB7864F0}.Release|Any CPU.Build.0 = Release|Any CPU + {E06268B5-C087-4508-86A7-8D8482759A0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E06268B5-C087-4508-86A7-8D8482759A0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E06268B5-C087-4508-86A7-8D8482759A0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E06268B5-C087-4508-86A7-8D8482759A0D}.Release|Any CPU.Build.0 = Release|Any CPU + {4096D2AB-1C9D-40B9-9F5C-7235089759A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4096D2AB-1C9D-40B9-9F5C-7235089759A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4096D2AB-1C9D-40B9-9F5C-7235089759A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4096D2AB-1C9D-40B9-9F5C-7235089759A5}.Release|Any CPU.Build.0 = Release|Any CPU + {E8397C32-5BD2-4DE8-BB29-96624A9C75A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8397C32-5BD2-4DE8-BB29-96624A9C75A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8397C32-5BD2-4DE8-BB29-96624A9C75A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8397C32-5BD2-4DE8-BB29-96624A9C75A0}.Release|Any CPU.Build.0 = Release|Any CPU + {8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -702,6 +734,11 @@ Global {477C2416-312D-46AE-BCD6-8FA1FAB43624} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79} {874F5A44-DB06-47AB-A18C-2D13942E0147} = {477C2416-312D-46AE-BCD6-8FA1FAB43624} {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC} = {D92BEAB2-60D6-4BB4-885A-6BA681C6CCF1} + {A70C589D-A498-4139-9CA9-CA82CB7864F0} = {71595D88-A05C-446E-B294-5CB6D91FB91F} + {E06268B5-C087-4508-86A7-8D8482759A0D} = {71595D88-A05C-446E-B294-5CB6D91FB91F} + {4096D2AB-1C9D-40B9-9F5C-7235089759A5} = {71595D88-A05C-446E-B294-5CB6D91FB91F} + {E8397C32-5BD2-4DE8-BB29-96624A9C75A0} = {71595D88-A05C-446E-B294-5CB6D91FB91F} + {8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7} = {71595D88-A05C-446E-B294-5CB6D91FB91F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E} diff --git a/NuGet.Config b/NuGet.Config index 54f245bd31..d188f6e5a0 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -4,10 +4,15 @@ + + + + + \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj index a98c7781cd..5b2db69a1a 100644 --- a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -1,5 +1,9 @@ + + + - + + diff --git a/src/apps/Elsa.ModularServer.Web/Program.cs b/src/apps/Elsa.ModularServer.Web/Program.cs index ec8e4f41d4..cc8af20265 100644 --- a/src/apps/Elsa.ModularServer.Web/Program.cs +++ b/src/apps/Elsa.ModularServer.Web/Program.cs @@ -1,6 +1,13 @@ +using CShells.AspNetCore.Extensions; + var builder = WebApplication.CreateBuilder(args); -var app = builder.Build(); +var services = builder.Services; -app.MapGet("/", () => "Hello World!"); +services.AddHealthChecks(); +builder.AddShells(); + +var app = builder.Build(); +app.MapHealthChecks("/"); +app.MapShells(); app.Run(); \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json index 10f68b8c8b..a0979b0b7b 100644 --- a/src/apps/Elsa.ModularServer.Web/appsettings.json +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -5,5 +5,16 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "CShells": { + "Shells": [{ + "Name": "Default", + "Properties": { + "WebRouting": { + "Path": "foo" + } + }, + "Features": [] + }] + } } diff --git a/src/common/Elsa.Features/Elsa.Features.csproj b/src/common/Elsa.Features/Elsa.Features.csproj index 10a3eaeb81..f9ea3d9b30 100644 --- a/src/common/Elsa.Features/Elsa.Features.csproj +++ b/src/common/Elsa.Features/Elsa.Features.csproj @@ -8,8 +8,13 @@ + + + + + diff --git a/src/modules/Elsa.Common/Elsa.Common.csproj b/src/modules/Elsa.Common/Elsa.Common.csproj index 986cf2ff49..bc21418fa2 100644 --- a/src/modules/Elsa.Common/Elsa.Common.csproj +++ b/src/modules/Elsa.Common/Elsa.Common.csproj @@ -15,6 +15,7 @@ + diff --git a/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs b/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs index b4c8a42b29..473aa01e4a 100644 --- a/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs +++ b/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs @@ -12,43 +12,46 @@ namespace Elsa.Extensions; /// public static class DependencyInjectionExtensions { - /// - /// Adds a memory store for the specified entity. - /// - public static IServiceCollection AddMemoryStore(this IServiceCollection services) where TStore : class + extension(IServiceCollection services) { - services.TryAddSingleton>(); - services.TryAddScoped(); - return services; - } + /// + /// Adds a memory store for the specified entity. + /// + public IServiceCollection AddMemoryStore() where TStore : class + { + services.TryAddSingleton>(); + services.TryAddScoped(); + return services; + } - /// - /// Adds a serialization options configurator for the specified type. - /// - public static IServiceCollection AddSerializationOptionsConfigurator(this IServiceCollection services) where T : class, ISerializationOptionsConfigurator - { - services.AddSingleton(); - return services; - } - - public static IServiceCollection AddStartupTask(this IServiceCollection services) where T : class, IStartupTask - { - return services.AddScoped(); - } - - public static IServiceCollection AddBackgroundTask(this IServiceCollection services) where T : class, IBackgroundTask - { - return services.AddScoped(); - } - - public static IServiceCollection AddRecurringTask(this IServiceCollection services) where T : class, IRecurringTask - { - return services.AddScoped(); - } - - public static IServiceCollection AddRecurringTask(this IServiceCollection services, TimeSpan interval) where T : class, IRecurringTask - { - services.Configure(options => options.Schedule.ConfigureTask(interval)); - return services.AddRecurringTask(); + /// + /// Adds a serialization options configurator for the specified type. + /// + public IServiceCollection AddSerializationOptionsConfigurator() where T : class, ISerializationOptionsConfigurator + { + services.AddSingleton(); + return services; + } + + public IServiceCollection AddStartupTask() where T : class, IStartupTask + { + return services.AddScoped(); + } + + public IServiceCollection AddBackgroundTask() where T : class, IBackgroundTask + { + return services.AddScoped(); + } + + public IServiceCollection AddRecurringTask() where T : class, IRecurringTask + { + return services.AddScoped(); + } + + public IServiceCollection AddRecurringTask(TimeSpan interval) where T : class, IRecurringTask + { + services.Configure(options => options.Schedule.ConfigureTask(interval)); + return services.AddRecurringTask(); + } } } \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs b/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs new file mode 100644 index 0000000000..930bfb1a87 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; +using CShells.Features; +using Elsa.Common.Serialization; +using Elsa.Common.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +public class DefaultFormattersFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + TypeDescriptor.AddAttributes(typeof(Type), new TypeConverterAttribute(typeof(TypeTypeConverter))); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs b/src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs new file mode 100644 index 0000000000..f4f355b4c5 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs @@ -0,0 +1,17 @@ +using CShells.Features; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +/// +/// Adds and configures the Mediator feature. +/// +public class MediatorFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddMediator() + .AddMediatorHostedServices(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs new file mode 100644 index 0000000000..ce6a7ac57b --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs @@ -0,0 +1,44 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Common.Multitenancy.EventHandlers; +using Elsa.Common.Multitenancy.HostedServices; +using Elsa.Common.RecurringTasks; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +public class MultitenancyFeature : IShellFeature +{ + private Func _tenantsProviderFactory = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + + // Order is important: Startup task first, then background and recurring tasks. + .AddSingleton() + + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) + + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) + + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(_tenantsProviderFactory) + + .AddHostedService() + ; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs b/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs new file mode 100644 index 0000000000..3d75e87b30 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using Elsa.Common.Codecs; +using Elsa.Common.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +[UsedImplicitly] +public class StringCompressionFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs b/src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs new file mode 100644 index 0000000000..93dd9831e7 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs @@ -0,0 +1,16 @@ +using CShells.Features; +using Elsa.Common.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +/// +/// Configures the system clock. +/// +public class SystemClockFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs b/src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs new file mode 100644 index 0000000000..94032e21d8 --- /dev/null +++ b/src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs @@ -0,0 +1,19 @@ +using CShells.Features; +using Elsa.Expressions.Contracts; +using Elsa.Expressions.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.ShellFeatures; + +/// +/// Installs and configures the expressions feature. +/// +public class ExpressionsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddScoped() + .AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj b/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj index c24e99f046..74eb3c854b 100644 --- a/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj +++ b/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs new file mode 100644 index 0000000000..c529df84d4 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs @@ -0,0 +1,16 @@ +using CShells.Features; +using Elsa.Extensions; +using Elsa.Workflows.CommitStates; +using Elsa.Workflows.CommitStates.Tasks; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.ShellFeatures; + +public class CommitStrategiesFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddStartupTask(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs new file mode 100644 index 0000000000..fccf9dd06a --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs @@ -0,0 +1,183 @@ +using CShells.Features; +using Elsa.Common; +using Elsa.Common.Serialization; +using Elsa.Common.ShellFeatures; +using Elsa.Expressions.ShellFeatures; +using Elsa.Extensions; +using Elsa.Workflows.ActivationValidators; +using Elsa.Workflows.Activities.Flowchart.Options; +using Elsa.Workflows.Activities.Flowchart.Serialization; +using Elsa.Workflows.Builders; +using Elsa.Workflows.CommitStates; +using Elsa.Workflows.IncidentStrategies; +using Elsa.Workflows.LogPersistence; +using Elsa.Workflows.LogPersistence.Strategies; +using Elsa.Workflows.Middleware.Activities; +using Elsa.Workflows.Middleware.Workflows; +using Elsa.Workflows.Pipelines.ActivityExecution; +using Elsa.Workflows.Pipelines.WorkflowExecution; +using Elsa.Workflows.PortResolvers; +using Elsa.Workflows.Serialization.Configurators; +using Elsa.Workflows.Serialization.Helpers; +using Elsa.Workflows.Serialization.Serializers; +using Elsa.Workflows.Services; +using Elsa.Workflows.UIHints.CheckList; +using Elsa.Workflows.UIHints.Dictionary; +using Elsa.Workflows.UIHints.Dropdown; +using Elsa.Workflows.UIHints.JsonEditor; +using Elsa.Workflows.UIHints.RadioList; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.ShellFeatures; + +[ShellFeature(DependsOn = +[ + nameof(SystemClockFeature), + nameof(ExpressionsFeature), + nameof(MediatorFeature), + nameof(DefaultFormattersFeature), + nameof(MultitenancyFeature), + nameof(CommitStrategiesFeature), +])] +public class WorkflowsFeature : IShellFeature +{ + /// + /// A delegate to configure the . + /// + public Action WorkflowExecutionPipeline { get; set; } = builder => builder + .UseExceptionHandling() + .UseDefaultActivityScheduler(); + + /// + /// A delegate to configure the . + /// + public Action ActivityExecutionPipeline { get; set; } = builder => builder.UseDefaultActivityInvoker(); + + /// + /// A factory that instantiates a concrete . + /// + public Func StandardInStreamProvider { get; set; } = _ => new StandardInStreamProvider(Console.In); + + /// + /// A factory that instantiates a concrete . + /// + public Func StandardOutStreamProvider { get; set; } = _ => new StandardOutStreamProvider(Console.Out); + + /// + /// A factory that instantiates a concrete . + /// + public Func> WorkflowLoggerStateGenerator { get; set; } = sp => new WorkflowLoggerStateGenerator(); + + /// + /// A factory that instantiates a concrete . + /// + public Func> ActivityLoggerStateGenerator { get; set; } = sp => new ActivityLoggerStateGenerator(); + + public void ConfigureServices(IServiceCollection services) + { + services + // Core. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(sp => ActivatorUtilities.CreateInstance(sp)) + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddTransient() + .AddScoped(typeof(Func), sp => () => sp.GetRequiredService()) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Incident Strategies. + .AddTransient() + .AddTransient() + + // Pipelines. + .AddScoped(sp => new ActivityExecutionPipeline(sp, ActivityExecutionPipeline)) + .AddScoped(sp => new WorkflowExecutionPipeline(sp, WorkflowExecutionPipeline)) + + // Built-in activity services. + .AddScoped() + .AddScoped() + .AddSerializationOptionsConfigurator() + .AddSerializationOptionsConfigurator() + + // Domain event handlers. + .AddHandlersFrom() + + // Stream providers. + .AddScoped(StandardInStreamProvider) + .AddScoped(StandardOutStreamProvider) + + // Storage drivers. + .AddScoped() + .AddStorageDriver() + .AddStorageDriver() + .AddStorageDriver() + + // Serialization. + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + + // Instantiation strategies. + .AddScoped() + + // UI. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + + // Logger state generators. + .AddSingleton(WorkflowLoggerStateGenerator) + .AddSingleton(ActivityLoggerStateGenerator) + + // Log Persistence Strategies. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Logging + .AddLogging(); + + // Flowchart + services.AddSerializationOptionsConfigurator(); + + // Register FlowchartOptions + services.AddOptions(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs new file mode 100644 index 0000000000..740b7dc1d4 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs @@ -0,0 +1,132 @@ +using System.Dynamic; +using System.Text.Json; +using System.Text.Json.Nodes; +using CShells.Features; +using Elsa.Caching.Features; +using Elsa.Common.Features; +using Elsa.Expressions.Contracts; +using Elsa.Extensions; +using Elsa.Features.Attributes; +using Elsa.Workflows.Features; +using Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity; +using Elsa.Workflows.Management.Contracts; +using Elsa.Workflows.Management.Entities; +using Elsa.Workflows.Management.Features; +using Elsa.Workflows.Management.Handlers.Notifications; +using Elsa.Workflows.Management.Mappers; +using Elsa.Workflows.Management.Materializers; +using Elsa.Workflows.Management.Models; +using Elsa.Workflows.Management.Options; +using Elsa.Workflows.Management.Providers; +using Elsa.Workflows.Management.Services; +using Elsa.Workflows.Management.Stores; +using Elsa.Workflows.Serialization.Serializers; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Installs and configures the workflow management feature. +/// +[DependsOn(typeof(StringCompressionFeature))] +[DependsOn(typeof(MediatorFeature))] +[DependsOn(typeof(MemoryCacheFeature))] +[DependsOn(typeof(SystemClockFeature))] +[DependsOn(typeof(WorkflowsFeature))] +[DependsOn(typeof(WorkflowDefinitionsFeature))] +[DependsOn(typeof(WorkflowInstancesFeature))] +[UsedImplicitly] +public class WorkflowManagementFeature : IShellFeature +{ + private const string PrimitivesCategory = "Primitives"; + private const string LookupsCategory = "Lookups"; + private const string DynamicCategory = "Dynamic"; + private const string DataCategory = "Data"; + private const string SystemCategory = "System"; + + private Func WorkflowDefinitionStore { get; set; } = sp => sp.GetRequiredService(); + private Func WorkflowInstanceStore { get; set; } = sp => sp.GetRequiredService(); + private Func WorkflowDefinitionPublisher { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + private Func WorkflowReferenceQuery { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A set of variable types to make available to the system. + /// + public HashSet VariableDescriptors { get; } = + [ + new(typeof(object), PrimitivesCategory, "The root class for all object in the CLR System."), + new(typeof(string), PrimitivesCategory, "Represents a static string of characters."), + new(typeof(bool), PrimitivesCategory, "Represents a true or false value."), + new(typeof(int), PrimitivesCategory, "A 32 bit integer."), + new(typeof(long), PrimitivesCategory, "A 64 bit integer."), + new(typeof(float), PrimitivesCategory, "A 32 bit floating point number."), + new(typeof(double), PrimitivesCategory, "A 64 bit floating point number."), + new(typeof(decimal), PrimitivesCategory, "A decimal number."), + new(typeof(Guid), PrimitivesCategory, "Represents a Globally Unique Identifier."), + new(typeof(DateTime), PrimitivesCategory, "A value type that represents a date and time."), + new(typeof(DateTimeOffset), PrimitivesCategory, "A value type that consists of a DateTime and a time zone offset."), + new(typeof(TimeSpan), PrimitivesCategory, "Represents a duration of time."), + new(typeof(IDictionary), LookupsCategory, "A dictionary with string key and values."), + new(typeof(IDictionary), LookupsCategory, "A dictionary with string key and object values."), + new(typeof(ExpandoObject), DynamicCategory, "A dictionary that can be typed as dynamic to access members using dot notation."), + new(typeof(JsonElement), DynamicCategory, "A JSON element for reading a JSON structure."), + new(typeof(JsonNode), DynamicCategory, "A JSON node for reading and writing a JSON structure."), + new(typeof(JsonObject), DynamicCategory, "A JSON object for reading and writing a JSON structure."), + new(typeof(byte[]), DataCategory, "A byte array."), + new(typeof(Stream), DataCategory, "A stream.") + ]; + + + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryStore() + .AddMemoryStore() + .AddActivityProvider() + .AddActivityProvider() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(WorkflowReferenceQuery) + .AddScoped(WorkflowDefinitionPublisher) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddSerializationOptionsConfigurator() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddScoped(WorkflowInstanceStore) + .AddScoped(WorkflowDefinitionStore) + .AddSingleton() + .Decorate() + .Decorate() + .AddNotificationHandler(); + + services + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + ; + + services.Configure(options => + { + foreach (var descriptor in VariableDescriptors.DistinctBy(x => x.Type)) + options.VariableDescriptors.Add(descriptor); + }); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs new file mode 100644 index 0000000000..6a66aacc99 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs @@ -0,0 +1,271 @@ +using CShells.Features; +using Elsa.Common.RecurringTasks; +using Elsa.Extensions; +using Elsa.Mediator.Contracts; +using Elsa.Workflows.Management; +using Elsa.Workflows.Management.Contracts; +using Elsa.Workflows.Management.Services; +using Elsa.Workflows.Runtime.ActivationValidators; +using Elsa.Workflows.Runtime.Entities; +using Elsa.Workflows.Runtime.Handlers; +using Elsa.Workflows.Runtime.Options; +using Elsa.Workflows.Runtime.Providers; +using Elsa.Workflows.Runtime.Stores; +using Elsa.Workflows.Runtime.Tasks; +using Elsa.Workflows.Runtime.UIHints; +using Medallion.Threading; +using Medallion.Threading.FileSystem; +using Microsoft.Extensions.DependencyInjection; +using WorkflowsFeature = Elsa.Workflows.ShellFeatures.WorkflowsFeature; + +namespace Elsa.Workflows.Runtime.ShellFeatures; + +/// +/// Installs and configures workflow runtime features. +/// +[ShellFeature(DependsOn = [nameof(WorkflowsFeature)])] +public class WorkflowRuntimeFeature : IShellFeature +{ + private IDictionary WorkflowDispatcherChannels { get; set; } = new Dictionary(); + + /// + /// A list of workflow builders configured during application startup. + /// + public IDictionary>> Workflows { get; set; } = new Dictionary>>(); + + /// + /// A factory that instantiates a concrete . + /// + public Func WorkflowRuntime { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowDispatcher { get; set; } = sp => + { + var decoratedService = ActivatorUtilities.CreateInstance(sp); + return ActivatorUtilities.CreateInstance(sp, decoratedService); + }; + + /// + /// A factory that instantiates an . + /// + public Func StimulusDispatcher { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowCancellationDispatcher { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates an . + /// + public Func BookmarkStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func BookmarkQueueStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func TriggerStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowExecutionLogStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func ActivityExecutionLogStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func DistributedLockProvider { get; set; } = _ => new FileDistributedSynchronizationProvider(new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "App_Data/locks"))); + + /// + /// A factory that instantiates an . + /// + public Func RunTaskDispatcher { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func BackgroundActivityScheduler { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates a log record sink for an . + /// + public Func> ActivityExecutionLogSink { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates a log record sink for an . + /// + public Func> WorkflowExecutionLogSink { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func DispatchWorkflowCommandHandler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowResumer { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func BookmarkQueueWorker { get; set; } = sp => sp.GetRequiredService(); + + + public void ConfigureServices(IServiceCollection services) + { + // Options. + services.Configure(options => { options.Workflows = Workflows; }); + services.Configure(options => + { + options.Channels.AddRange(WorkflowDispatcherChannels.Values); + }); + services.Configure(options => + { + options.Schedule.ConfigureTask(TimeSpan.FromSeconds(10)); + }); + + services + // Core. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(WorkflowRuntime) + .AddScoped(WorkflowDispatcher) + .AddScoped(StimulusDispatcher) + .AddScoped(WorkflowCancellationDispatcher) + .AddScoped(RunTaskDispatcher) + .AddScoped(ActivityExecutionLogSink) + .AddScoped(WorkflowExecutionLogSink) + .AddSingleton(BackgroundActivityScheduler) + .AddSingleton() + .AddSingleton() + .AddScoped(BookmarkQueueWorker) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(WorkflowResumer) + .AddScoped() + .AddScoped(BookmarkQueueWorker) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped, WorkflowExecutionLogRecordExtractor>() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Deprecated services. + .AddScoped() + + // Stores. + .AddScoped(BookmarkStore) + .AddScoped(BookmarkQueueStore) + .AddScoped(TriggerStore) + .AddScoped(WorkflowExecutionLogStore) + .AddScoped(ActivityExecutionLogStore) + + // Lazy services. + .AddScoped>>(sp => sp.GetServices) + .AddScoped>>(sp => sp.GetServices) + + // Noop stores. + .AddScoped() + .AddScoped() + + // Memory stores. + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore() + + // Startup tasks, background tasks, and recurring tasks. + .AddStartupTask() + .AddRecurringTask(TimeSpan.FromMinutes(1)) + .AddRecurringTask(TimeSpan.FromSeconds(10)) + .AddRecurringTask(TimeSpan.FromMinutes(5)) // Same default as the workflow liveness threshold. + + // Distributed locking. + .AddSingleton(DistributedLockProvider) + + // Workflow definition providers. + .AddWorkflowDefinitionProvider() + + // UI property handlers. + .AddScoped() + + // Domain handlers. + .AddCommandHandler() + .AddCommandHandler() + .AddCommandHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + + // Workflow activation strategies. + .AddScoped() + .AddScoped() + .AddScoped() + ; + + services + // Decorators. + .Decorate() + + // Handlers. + .AddNotificationHandler() + .AddNotificationHandler(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa/Elsa.csproj b/src/modules/Elsa/Elsa.csproj index 08ccd9fb78..7dab0dff31 100644 --- a/src/modules/Elsa/Elsa.csproj +++ b/src/modules/Elsa/Elsa.csproj @@ -9,16 +9,16 @@ - - - + + + - - - - + + + + diff --git a/src/modules/Elsa/ShellFeatures/ElsaFeature.cs b/src/modules/Elsa/ShellFeatures/ElsaFeature.cs new file mode 100644 index 0000000000..9ecfb497ec --- /dev/null +++ b/src/modules/Elsa/ShellFeatures/ElsaFeature.cs @@ -0,0 +1,17 @@ +using CShells.Features; +using Elsa.Workflows.Management.ShellFeatures; +using Elsa.Workflows.Runtime.ShellFeatures; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.ShellFeatures; + +[ShellFeature(DependsOn = [ + nameof(WorkflowManagementFeature), + nameof(WorkflowRuntimeFeature) +])] +public class ElsaFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} \ No newline at end of file From 91d2e9d5ba2bab8395aa2950cdefd5efe356167d Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 7 Jan 2026 14:48:57 +0100 Subject: [PATCH 07/36] Refactor CShells: enhance pipeline configuration and features Consolidated updates to CShells including a new `ResolverPipelineBuilder` for customizable resolver strategy pipelines. Improved assembly scanning, error handling in web routing, and streamlined shell feature dependencies for better clarity and functionality. --- .../Elsa.ModularServer.Web/appsettings.json | 2 +- src/modules/Elsa.Caching/Elsa.Caching.csproj | 4 ++++ .../ShellFeatures/MultitenancyFeature.cs | 16 +++++--------- .../ShellFeatures/WorkflowsFeature.cs | 22 +++++++++---------- .../WorkflowManagementFeature.cs | 17 ++++++++------ .../ShellFeatures/WorkflowRuntimeFeature.cs | 3 +-- src/modules/Elsa/ShellFeatures/ElsaFeature.cs | 6 ++--- 7 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json index a0979b0b7b..b98dd6d8e8 100644 --- a/src/apps/Elsa.ModularServer.Web/appsettings.json +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -14,7 +14,7 @@ "Path": "foo" } }, - "Features": [] + "Features": ["Elsa"] }] } } diff --git a/src/modules/Elsa.Caching/Elsa.Caching.csproj b/src/modules/Elsa.Caching/Elsa.Caching.csproj index ab4269db6b..f2834574eb 100644 --- a/src/modules/Elsa.Caching/Elsa.Caching.csproj +++ b/src/modules/Elsa.Caching/Elsa.Caching.csproj @@ -16,4 +16,8 @@ + + + + diff --git a/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs index ce6a7ac57b..b02cb5ae7e 100644 --- a/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs +++ b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs @@ -9,7 +9,7 @@ namespace Elsa.Common.ShellFeatures; public class MultitenancyFeature : IShellFeature { - private Func _tenantsProviderFactory = sp => sp.GetRequiredService(); + private readonly Func _tenantsProviderFactory = sp => sp.GetRequiredService(); public void ConfigureServices(IServiceCollection services) { @@ -19,16 +19,10 @@ public void ConfigureServices(IServiceCollection services) .AddSingleton() .AddSingleton() - // Order is important: Startup task first, then background and recurring tasks. - .AddSingleton() - - .AddSingleton() - .AddSingleton(sp => sp.GetRequiredService()) - .AddSingleton(sp => sp.GetRequiredService()) - - .AddSingleton() - .AddSingleton(sp => sp.GetRequiredService()) - .AddSingleton(sp => sp.GetRequiredService()) + // TenantTaskManager handles all task lifecycle in the correct order + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) .AddSingleton() .AddSingleton() diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs index fccf9dd06a..273b003116 100644 --- a/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs @@ -32,12 +32,12 @@ namespace Elsa.Workflows.ShellFeatures; [ShellFeature(DependsOn = [ - nameof(SystemClockFeature), - nameof(ExpressionsFeature), - nameof(MediatorFeature), - nameof(DefaultFormattersFeature), - nameof(MultitenancyFeature), - nameof(CommitStrategiesFeature), + "SystemClock", + "Expressions", + "Mediator", + "DefaultFormatters", + "Multitenancy", + "CommitStrategies" ])] public class WorkflowsFeature : IShellFeature { @@ -173,11 +173,11 @@ public void ConfigureServices(IServiceCollection services) // Logging .AddLogging(); - - // Flowchart - services.AddSerializationOptionsConfigurator(); - // Register FlowchartOptions - services.AddOptions(); + // Flowchart + services.AddSerializationOptionsConfigurator(); + + // Register FlowchartOptions + services.AddOptions(); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs index 740b7dc1d4..81c11f5050 100644 --- a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs @@ -29,13 +29,16 @@ namespace Elsa.Workflows.Management.ShellFeatures; /// /// Installs and configures the workflow management feature. /// -[DependsOn(typeof(StringCompressionFeature))] -[DependsOn(typeof(MediatorFeature))] -[DependsOn(typeof(MemoryCacheFeature))] -[DependsOn(typeof(SystemClockFeature))] -[DependsOn(typeof(WorkflowsFeature))] -[DependsOn(typeof(WorkflowDefinitionsFeature))] -[DependsOn(typeof(WorkflowInstancesFeature))] +[ShellFeature(DependsOn = +[ + "StringCompression", + "Mediator", + "MemoryCache", + "SystemClock", + "Workflows", + "WorkflowDefinitions", + "WorkflowInstances" +])] [UsedImplicitly] public class WorkflowManagementFeature : IShellFeature { diff --git a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs index 6a66aacc99..c946f5376a 100644 --- a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs +++ b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs @@ -16,14 +16,13 @@ using Medallion.Threading; using Medallion.Threading.FileSystem; using Microsoft.Extensions.DependencyInjection; -using WorkflowsFeature = Elsa.Workflows.ShellFeatures.WorkflowsFeature; namespace Elsa.Workflows.Runtime.ShellFeatures; /// /// Installs and configures workflow runtime features. /// -[ShellFeature(DependsOn = [nameof(WorkflowsFeature)])] +[ShellFeature(DependsOn = ["Workflows"])] public class WorkflowRuntimeFeature : IShellFeature { private IDictionary WorkflowDispatcherChannels { get; set; } = new Dictionary(); diff --git a/src/modules/Elsa/ShellFeatures/ElsaFeature.cs b/src/modules/Elsa/ShellFeatures/ElsaFeature.cs index 9ecfb497ec..9ddd3e9eb6 100644 --- a/src/modules/Elsa/ShellFeatures/ElsaFeature.cs +++ b/src/modules/Elsa/ShellFeatures/ElsaFeature.cs @@ -1,13 +1,11 @@ using CShells.Features; -using Elsa.Workflows.Management.ShellFeatures; -using Elsa.Workflows.Runtime.ShellFeatures; using Microsoft.Extensions.DependencyInjection; namespace Elsa.ShellFeatures; [ShellFeature(DependsOn = [ - nameof(WorkflowManagementFeature), - nameof(WorkflowRuntimeFeature) + "WorkflowManagement", + "WorkflowRuntime" ])] public class ElsaFeature : IShellFeature { From 49d035d8f6b674c461a33387b33f5335c672a733 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 15 Jan 2026 20:51:43 +0100 Subject: [PATCH 08/36] Add feature registration system and FastEndpoints integration Introduced a feature registration infrastructure with `IInstalledFeatureProvider` and related implementations. Added shell-based feature configurations such as caching, SAS tokens, workflows management, and a FastEndpoints integration module to support dynamic API registration. --- .../Contracts/IInstalledFeatureProvider.cs | 28 ++++++++ .../Services/InstalledFeatureProvider.cs | 30 ++++++++ .../Services/ShellInstalledFeatureProvider.cs | 69 +++++++++++++++++++ .../ShellFeatures/MemoryCacheFeature.cs | 23 +++++++ .../ShellFeatures/SasTokensFeature.cs | 27 ++++++++ .../ShellFeatures/WorkflowsApiFeature.cs | 46 +++++++++++++ .../WorkflowDefinitionsFeature.cs | 23 +++++++ .../ShellFeatures/WorkflowInstancesFeature.cs | 23 +++++++ 8 files changed, 269 insertions(+) create mode 100644 src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs create mode 100644 src/common/Elsa.Features/Services/InstalledFeatureProvider.cs create mode 100644 src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs create mode 100644 src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs create mode 100644 src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs create mode 100644 src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs create mode 100644 src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs create mode 100644 src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs diff --git a/src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs b/src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs new file mode 100644 index 0000000000..926b377dc8 --- /dev/null +++ b/src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs @@ -0,0 +1,28 @@ +using Elsa.Features.Models; + +namespace Elsa.Features.Contracts; + +/// +/// Provides read-only access to installed features. +/// +/// +/// This interface is used to query the list of features that are installed and available in the application. +/// Unlike , this interface does not provide write operations, +/// making it suitable for implementations where features are discovered automatically (e.g., from shell features) +/// rather than manually registered. +/// +public interface IInstalledFeatureProvider +{ + /// + /// Gets all installed features. + /// + /// All installed features. + IEnumerable List(); + + /// + /// Finds a feature descriptor by its full name. + /// + /// The full name of the feature. + /// The feature descriptor or null if not found. + FeatureDescriptor? Find(string fullName); +} diff --git a/src/common/Elsa.Features/Services/InstalledFeatureProvider.cs b/src/common/Elsa.Features/Services/InstalledFeatureProvider.cs new file mode 100644 index 0000000000..bbba02882b --- /dev/null +++ b/src/common/Elsa.Features/Services/InstalledFeatureProvider.cs @@ -0,0 +1,30 @@ +using Elsa.Features.Contracts; +using Elsa.Features.Models; + +namespace Elsa.Features.Services; + +/// +/// Legacy implementation of that delegates to . +/// +/// +/// This implementation is used when features are manually registered using the legacy feature system. +/// For shell-based feature discovery, use ShellInstalledFeatureProvider instead. +/// +public class InstalledFeatureProvider : IInstalledFeatureProvider +{ + private readonly IInstalledFeatureRegistry _registry; + + /// + /// Initializes a new instance of the class. + /// + public InstalledFeatureProvider(IInstalledFeatureRegistry registry) + { + _registry = registry; + } + + /// + public IEnumerable List() => _registry.List(); + + /// + public FeatureDescriptor? Find(string fullName) => _registry.Find(fullName); +} diff --git a/src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs b/src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs new file mode 100644 index 0000000000..4b27ab1fee --- /dev/null +++ b/src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs @@ -0,0 +1,69 @@ +using CShells.Features; +using Elsa.Features.Contracts; +using Elsa.Features.Models; + +namespace Elsa.Features.Services; + +/// +/// Shell-based implementation of that reads from CShells feature descriptors. +/// +/// +/// This implementation automatically discovers features from the shell's feature descriptors, +/// mapping them to Elsa's model. Features are read from the +/// shell's DI container where they were registered during shell initialization. +/// +public class ShellInstalledFeatureProvider : IInstalledFeatureProvider +{ + private readonly IEnumerable _shellFeatures; + + /// + /// Initializes a new instance of the class. + /// + public ShellInstalledFeatureProvider(IEnumerable shellFeatures) + { + _shellFeatures = shellFeatures; + } + + /// + public IEnumerable List() + { + return _shellFeatures + .Where(sf => sf.StartupType != null) + .Select(MapToElsaFeatureDescriptor); + } + + /// + public FeatureDescriptor? Find(string fullName) + { + var shellFeature = _shellFeatures.FirstOrDefault(sf => + MapToFullName(sf) == fullName); + + return shellFeature != null ? MapToElsaFeatureDescriptor(shellFeature) : null; + } + + private static FeatureDescriptor MapToElsaFeatureDescriptor(ShellFeatureDescriptor shell) + { + // Extract namespace and name from shell feature + var type = shell.StartupType!; + var ns = type.Namespace ?? "Unknown"; + var name = shell.Id; + + // Try to get display name and description from metadata + // These can be set via ShellFeatureAttribute properties + var displayName = shell.Metadata.TryGetValue("DisplayName", out var dn) + ? dn.ToString() ?? name + : name; + + var description = shell.Metadata.TryGetValue("Description", out var desc) + ? desc.ToString() ?? string.Empty + : string.Empty; + + return new FeatureDescriptor(name, ns, displayName, description); + } + + private static string MapToFullName(ShellFeatureDescriptor shell) + { + var ns = shell.StartupType?.Namespace ?? "Unknown"; + return $"{ns}.{shell.Id}"; + } +} diff --git a/src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs b/src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs new file mode 100644 index 0000000000..6564c76dcd --- /dev/null +++ b/src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs @@ -0,0 +1,23 @@ +using CShells.Features; +using Elsa.Caching.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Caching.ShellFeatures; + +/// +/// Configures the MemoryCache. +/// +[ShellFeature] +[UsedImplicitly] +public class MemoryCacheFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryCache() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + } +} diff --git a/src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs b/src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs new file mode 100644 index 0000000000..0447e7569d --- /dev/null +++ b/src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs @@ -0,0 +1,27 @@ +using CShells.Features; +using Elsa.SasTokens.Contracts; +using JetBrains.Annotations; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.SasTokens.ShellFeatures; + +/// +/// Adds the SAS tokens feature to the workflow runtime. +/// +[ShellFeature( + DisplayName = "SAS Tokens", + Description = "Provides shared access signature token generation and validation")] +[UsedImplicitly] +public class SasTokensFeature : IShellFeature +{ + private Func TokenService { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + public void ConfigureServices(IServiceCollection services) + { + var builder = services.AddDataProtection(); + builder.SetApplicationName("Elsa Workflows"); + + services.AddScoped(TokenService); + } +} diff --git a/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs b/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs new file mode 100644 index 0000000000..9d33376072 --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs @@ -0,0 +1,46 @@ +using CShells.FastEndpoints.Features; +using CShells.Features; +using Elsa.Extensions; +using Elsa.Workflows.Api.Constants; +using Elsa.Workflows.Api.Requirements; +using Elsa.Workflows.Api.Serialization; +using Elsa.Workflows.Api.Services; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Api.ShellFeatures; + +/// +/// Adds workflows API features with FastEndpoints support. +/// +/// +/// This feature implements to indicate that this assembly +/// contains FastEndpoints that should be automatically discovered and registered. +/// +[ShellFeature( + DisplayName = "Workflows API", + Description = "Provides REST API endpoints for workflow management", + DependsOn = +[ + "FastEndpoints", + "WorkflowInstances", + "WorkflowManagement", + "WorkflowRuntime", + "SasTokens" +])] +[UsedImplicitly] +public class WorkflowsApiFeature : IFastEndpointsShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSerializationOptionsConfigurator(); + services.AddScoped(); + services.AddScoped(); + services.Configure(options => + { + options.AddPolicy(AuthorizationPolicies.NotReadOnlyPolicy, policy => policy.AddRequirements(new NotReadOnlyRequirement())); + }); + services.AddScoped(); + } +} diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs new file mode 100644 index 0000000000..aeec77e0bf --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs @@ -0,0 +1,23 @@ +using CShells.Features; +using Elsa.Workflows.Management.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Configures workflow definition storage. +/// +[ShellFeature( + DisplayName = "Workflow Definitions", + Description = "Manages workflow definitions and their storage")] +[UsedImplicitly] +public class WorkflowDefinitionsFeature : IShellFeature +{ + private Func WorkflowDefinitionStore { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(WorkflowDefinitionStore); + } +} diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs new file mode 100644 index 0000000000..accf031dc4 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs @@ -0,0 +1,23 @@ +using CShells.Features; +using Elsa.Workflows.Management.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Enables storage of workflow instances. +/// +[ShellFeature( + DisplayName = "Workflow Instances", + Description = "Manages workflow execution instances and their persistent state")] +[UsedImplicitly] +public class WorkflowInstancesFeature : IShellFeature +{ + private Func WorkflowInstanceStore { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(WorkflowInstanceStore); + } +} From f758b12810346d6d70385dd70662d388a4c4e285 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 15 Jan 2026 20:51:58 +0100 Subject: [PATCH 09/36] Refactor shell routing and enhance global route handling Refactored `ShellEndpointRouteBuilder` to simplify initialization and support a combined shell/global route prefix. Enhanced `ShellEndpointRegistrationHandler` to include global route prefix logic and improved feature discovery using pre-resolved descriptors. Updated `Program.cs` for consistent middleware setup. --- src/apps/Elsa.ModularServer.Web/Program.cs | 6 +++++- src/apps/Elsa.ModularServer.Web/appsettings.json | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/apps/Elsa.ModularServer.Web/Program.cs b/src/apps/Elsa.ModularServer.Web/Program.cs index cc8af20265..87d16e4f4f 100644 --- a/src/apps/Elsa.ModularServer.Web/Program.cs +++ b/src/apps/Elsa.ModularServer.Web/Program.cs @@ -3,11 +3,15 @@ var builder = WebApplication.CreateBuilder(args); var services = builder.Services; -services.AddHealthChecks(); builder.AddShells(); +services.AddHealthChecks(); +services.AddAuthorization(); +services.AddAuthentication(); var app = builder.Build(); app.MapHealthChecks("/"); +app.UseAuthorization(); +app.UseAuthentication(); app.MapShells(); app.Run(); \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json index b98dd6d8e8..69996b86b7 100644 --- a/src/apps/Elsa.ModularServer.Web/appsettings.json +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -14,7 +14,12 @@ "Path": "foo" } }, - "Features": ["Elsa"] + "Settings": { + "FastEndpoints": { + "GlobalRoutePrefix": "elsa/api" + } + }, + "Features": ["Elsa", "WorkflowsApi"] }] } } From f4bbaf9c1502d2ddfc704bae30b45a73a69c95b5 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 15 Jan 2026 20:52:47 +0100 Subject: [PATCH 10/36] Refactor feature endpoints to use `IInstalledFeatureProvider` for improved dependency management and simplified implementation --- .../Endpoints/Features/Get/Endpoint.cs | 14 +++----------- .../Endpoints/Features/List/Endpoint.cs | 12 ++---------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs index f0f1c0cf12..fd82297b98 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs @@ -9,16 +9,8 @@ namespace Elsa.Workflows.Api.Endpoints.Features.Get; /// Returns the specified installed feature. /// [PublicAPI] -internal class Get : ElsaEndpointWithoutRequest +internal class Get(IInstalledFeatureProvider installedFeatureProvider) : ElsaEndpointWithoutRequest { - private readonly IInstalledFeatureRegistry _installedFeatureRegistry; - - /// - public Get(IInstalledFeatureRegistry installedFeatureRegistry) - { - _installedFeatureRegistry = installedFeatureRegistry; - } - /// public override void Configure() { @@ -30,14 +22,14 @@ public override void Configure() public override async Task HandleAsync( CancellationToken cancellationToken) { var fullName = Route("fullName")!; - var descriptor = _installedFeatureRegistry.Find(fullName); + var descriptor = installedFeatureProvider.Find(fullName); if (descriptor == null) { await Send.NotFoundAsync(cancellationToken); return; } - + await Send.OkAsync(descriptor, cancellationToken); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs index dfa38c0583..b62efe712e 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs @@ -10,16 +10,8 @@ namespace Elsa.Workflows.Api.Endpoints.Features.List; /// Returns a list of installed features. /// [PublicAPI] -internal class List : ElsaEndpointWithoutRequest> +internal class List(IInstalledFeatureProvider installedFeatureProvider) : ElsaEndpointWithoutRequest> { - private readonly IInstalledFeatureRegistry _installedFeatureRegistry; - - /// - public List(IInstalledFeatureRegistry installedFeatureRegistry) - { - _installedFeatureRegistry = installedFeatureRegistry; - } - /// public override void Configure() { @@ -30,7 +22,7 @@ public override void Configure() /// public override Task> ExecuteAsync(CancellationToken cancellationToken) { - var descriptors = _installedFeatureRegistry.List().ToList(); + var descriptors = installedFeatureProvider.List().ToList(); var response = new ListResponse(descriptors); return Task.FromResult(response); From 50b55ff563a54ee266340423d7faef07c0e75c77 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 15 Jan 2026 20:52:56 +0100 Subject: [PATCH 11/36] Add display names, descriptions, and dependency enhancements to shell features Standardized `ShellFeature` attributes across `ElsaFeature`, `WorkflowRuntimeFeature`, and `WorkflowManagementFeature` by adding display names, descriptions, and improving dependency declarations. Updated `ElsaFeature` to register `IInstalledFeatureProvider` for feature bridging. --- .../WorkflowManagementFeature.cs | 23 +++++++++++-------- .../ShellFeatures/WorkflowRuntimeFeature.cs | 5 +++- src/modules/Elsa/ShellFeatures/ElsaFeature.cs | 7 +++++- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs index 81c11f5050..faf8144d6a 100644 --- a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs @@ -29,16 +29,19 @@ namespace Elsa.Workflows.Management.ShellFeatures; /// /// Installs and configures the workflow management feature. /// -[ShellFeature(DependsOn = -[ - "StringCompression", - "Mediator", - "MemoryCache", - "SystemClock", - "Workflows", - "WorkflowDefinitions", - "WorkflowInstances" -])] +[ShellFeature( + DisplayName = "Workflow Management", + Description = "Provides comprehensive workflow definition and instance management capabilities", + DependsOn = + [ + "StringCompression", + "Mediator", + "MemoryCache", + "SystemClock", + "Workflows", + "WorkflowDefinitions", + "WorkflowInstances" + ])] [UsedImplicitly] public class WorkflowManagementFeature : IShellFeature { diff --git a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs index c946f5376a..f801f354f5 100644 --- a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs +++ b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs @@ -22,7 +22,10 @@ namespace Elsa.Workflows.Runtime.ShellFeatures; /// /// Installs and configures workflow runtime features. /// -[ShellFeature(DependsOn = ["Workflows"])] +[ShellFeature( + DisplayName = "Workflow Runtime", + Description = "Provides workflow execution runtime and scheduling capabilities", + DependsOn = ["Workflows"])] public class WorkflowRuntimeFeature : IShellFeature { private IDictionary WorkflowDispatcherChannels { get; set; } = new Dictionary(); diff --git a/src/modules/Elsa/ShellFeatures/ElsaFeature.cs b/src/modules/Elsa/ShellFeatures/ElsaFeature.cs index 9ddd3e9eb6..815b06fe6e 100644 --- a/src/modules/Elsa/ShellFeatures/ElsaFeature.cs +++ b/src/modules/Elsa/ShellFeatures/ElsaFeature.cs @@ -3,7 +3,10 @@ namespace Elsa.ShellFeatures; -[ShellFeature(DependsOn = [ +[ShellFeature( + DisplayName = "Elsa Core", + Description = "Core Elsa workflow system functionality", + DependsOn = [ "WorkflowManagement", "WorkflowRuntime" ])] @@ -11,5 +14,7 @@ public class ElsaFeature : IShellFeature { public void ConfigureServices(IServiceCollection services) { + // Register the shell-based feature provider to bridge shell features to the Elsa feature API + services.AddSingleton(); } } \ No newline at end of file From 30548bdd63665ab94ce278c34683c46297de444b Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 15 Jan 2026 20:53:13 +0100 Subject: [PATCH 12/36] Add FastEndpoints references and update package versions Added project references to CShells.FastEndpoints and related projects in multiple csproj files. Updated FastEndpoints package versions in `Directory.Packages.props` for compatibility with .NET 8/9/10. Removed obsolete folder references from Elsa.Caching.csproj and refined the namespace in CShells.AspNetCore.Abstractions. --- Directory.Packages.props | 9 ++++++--- Elsa.sln | 18 ++++++++++++++++++ .../Elsa.ModularServer.Web.csproj | 2 ++ src/modules/Elsa.Caching/Elsa.Caching.csproj | 4 ---- .../Elsa.Workflows.Api.csproj | 1 + 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 1508a9239d..29087b94e8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,6 +5,9 @@ + + + @@ -43,6 +46,9 @@ + + + @@ -103,9 +109,6 @@ - - - diff --git a/Elsa.sln b/Elsa.sln index 37b0ac2c35..fef87e5333 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -326,6 +326,10 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.ModularServer.Web", "src\apps\Elsa.ModularServer.Web\Elsa.ModularServer.Web.csproj", "{E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cshells", "cshells", "{71595D88-A05C-446E-B294-5CB6D91FB91F}" + ProjectSection(SolutionItems) = preProject + ..\..\..\sfmskywalker\cshells\main\Directory.Packages.props = ..\..\..\sfmskywalker\cshells\main\Directory.Packages.props + ..\..\..\sfmskywalker\cshells\main\Directory.Build.props = ..\..\..\sfmskywalker\cshells\main\Directory.Build.props + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells", "..\..\..\sfmskywalker\cshells\main\src\CShells\CShells.csproj", "{A70C589D-A498-4139-9CA9-CA82CB7864F0}" EndProject @@ -343,6 +347,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Common.UnitTests", "te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Http.IntegrationTests", "test\integration\Elsa.Http.IntegrationTests\Elsa.Http.IntegrationTests.csproj", "{8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.FastEndpoints.Abstractions", "..\..\..\sfmskywalker\cshells\main\src\CShells.FastEndpoints.Abstractions\CShells.FastEndpoints.Abstractions.csproj", "{BE744448-F7F9-4807-BA5B-47D3463E4CF6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.FastEndpoints", "..\..\..\sfmskywalker\cshells\main\src\CShells.FastEndpoints\CShells.FastEndpoints.csproj", "{690B7E4E-62C1-409B-BB9B-F8F55051CA27}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -635,6 +643,14 @@ Global {8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E}.Debug|Any CPU.Build.0 = Debug|Any CPU {8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E}.Release|Any CPU.ActiveCfg = Release|Any CPU {8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E}.Release|Any CPU.Build.0 = Release|Any CPU + {BE744448-F7F9-4807-BA5B-47D3463E4CF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE744448-F7F9-4807-BA5B-47D3463E4CF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE744448-F7F9-4807-BA5B-47D3463E4CF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE744448-F7F9-4807-BA5B-47D3463E4CF6}.Release|Any CPU.Build.0 = Release|Any CPU + {690B7E4E-62C1-409B-BB9B-F8F55051CA27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {690B7E4E-62C1-409B-BB9B-F8F55051CA27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {690B7E4E-62C1-409B-BB9B-F8F55051CA27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {690B7E4E-62C1-409B-BB9B-F8F55051CA27}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -745,6 +761,8 @@ Global {B8006D70-1630-43DB-A043-FA89FAC70F37} = {18453B51-25EB-4317-A4B3-B10518252E92} {A3C07D5B-2A30-494E-B9BC-4B1594B31ABC} = {18453B51-25EB-4317-A4B3-B10518252E92} {8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E} = {1B8D5897-902E-4632-8698-E89CAF3DDF54} + {BE744448-F7F9-4807-BA5B-47D3463E4CF6} = {71595D88-A05C-446E-B294-5CB6D91FB91F} + {690B7E4E-62C1-409B-BB9B-F8F55051CA27} = {71595D88-A05C-446E-B294-5CB6D91FB91F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E} diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj index 5b2db69a1a..18c1865392 100644 --- a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -4,6 +4,8 @@ + + diff --git a/src/modules/Elsa.Caching/Elsa.Caching.csproj b/src/modules/Elsa.Caching/Elsa.Caching.csproj index f2834574eb..ab4269db6b 100644 --- a/src/modules/Elsa.Caching/Elsa.Caching.csproj +++ b/src/modules/Elsa.Caching/Elsa.Caching.csproj @@ -16,8 +16,4 @@ - - - - diff --git a/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj b/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj index 9311d394cf..a0f05c1465 100644 --- a/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj +++ b/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj @@ -8,6 +8,7 @@ + From c95e1fefddf558fc17045356474d0dc4e6148180 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 15 Jan 2026 21:08:07 +0100 Subject: [PATCH 13/36] Add Identity and DefaultAuthentication features to appsettings.json configuration --- src/apps/Elsa.ModularServer.Web/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json index 69996b86b7..d01934f565 100644 --- a/src/apps/Elsa.ModularServer.Web/appsettings.json +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -19,7 +19,7 @@ "GlobalRoutePrefix": "elsa/api" } }, - "Features": ["Elsa", "WorkflowsApi"] + "Features": ["Elsa", "WorkflowsApi", "Identity", "DefaultAuthentication"] }] } } From 98415cd5ac9fa1fb7700a52f39d09da11b00fde7 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 15 Jan 2026 21:08:13 +0100 Subject: [PATCH 14/36] Add project references for Elsa.Identity and CShells.FastEndpoints.Abstractions --- src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj | 1 + src/modules/Elsa.Identity/Elsa.Identity.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj index 18c1865392..a4776000ab 100644 --- a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -5,6 +5,7 @@ + diff --git a/src/modules/Elsa.Identity/Elsa.Identity.csproj b/src/modules/Elsa.Identity/Elsa.Identity.csproj index 7f1bbc5e34..33ab4e5906 100644 --- a/src/modules/Elsa.Identity/Elsa.Identity.csproj +++ b/src/modules/Elsa.Identity/Elsa.Identity.csproj @@ -12,6 +12,7 @@ + From 331b6052a2f1188eb03361aef9275d1282dde64f Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 15 Jan 2026 21:08:20 +0100 Subject: [PATCH 15/36] Add `Identity` and `DefaultAuthentication` shell features with enhanced authentication and authorization support --- .../DefaultAuthenticationFeature.cs | 73 +++++++++++ .../ShellFeatures/IdentityFeature.cs | 119 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs create mode 100644 src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs diff --git a/src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs b/src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs new file mode 100644 index 0000000000..c317830572 --- /dev/null +++ b/src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs @@ -0,0 +1,73 @@ +using AspNetCore.Authentication.ApiKey; +using CShells.Features; +using Elsa.Extensions; +using Elsa.Identity.Providers; +using Elsa.Requirements; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Identity.ShellFeatures; + +/// +/// Provides an authorization feature that configures the system with JWT bearer and API key authentication. +/// +[ShellFeature( + DisplayName = "Default Authentication", + Description = "Provides JWT bearer and API key authentication", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class DefaultAuthenticationFeature : IShellFeature +{ + private const string MultiScheme = "Jwt-or-ApiKey"; + + /// + /// Gets or sets the API key provider type. + /// + public Type ApiKeyProviderType { get; set; } = typeof(DefaultApiKeyProvider); + + /// + /// Gets or sets whether to require localhost for the security root policy. + /// + public bool RequireLocalHost { get; set; } = true; + + public void ConfigureServices(IServiceCollection services) + { + services.ConfigureOptions(); + services.ConfigureOptions(); + + var authBuilder = services + .AddAuthentication(MultiScheme) + .AddPolicyScheme(MultiScheme, MultiScheme, options => + { + options.ForwardDefaultSelector = context => + { + return context.Request.Headers.Authorization.Any(x => x!.Contains(ApiKeyDefaults.AuthenticationScheme)) + ? ApiKeyDefaults.AuthenticationScheme + : JwtBearerDefaults.AuthenticationScheme; + }; + }) + .AddJwtBearer(); + + // Configure API key authorization based on provider type + if (ApiKeyProviderType == typeof(AdminApiKeyProvider)) + authBuilder.AddApiKeyInAuthorizationHeader(); + else + authBuilder.AddApiKeyInAuthorizationHeader(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(ApiKeyProviderType); + services.AddScoped(sp => (IApiKeyProvider)sp.GetRequiredService(ApiKeyProviderType)); + + services.AddAuthorization(options => + { + if (RequireLocalHost) + options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.AddRequirements(new LocalHostPermissionRequirement())); + else + options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.RequireAuthenticatedUser()); + }); + } +} diff --git a/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs b/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs new file mode 100644 index 0000000000..74e13f65fb --- /dev/null +++ b/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs @@ -0,0 +1,119 @@ +using AspNetCore.Authentication.ApiKey; +using CShells.FastEndpoints.Features; +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Extensions; +using Elsa.Identity.Contracts; +using Elsa.Identity.Entities; +using Elsa.Identity.Multitenancy; +using Elsa.Identity.Options; +using Elsa.Identity.Providers; +using Elsa.Identity.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Identity.ShellFeatures; + +/// +/// Provides identity feature to authenticate & authorize API requests. +/// +[ShellFeature( + DisplayName = "Identity", + Description = "Provides identity management, authentication and authorization capabilities", + DependsOn = ["SystemClock"])] +[UsedImplicitly] +public class IdentityFeature : IFastEndpointsShellFeature +{ + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func UserStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func ApplicationStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func RoleStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func UserProvider { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func ApplicationProvider { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func RoleProvider { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + // Configure options with defaults + services.Configure(_ => { }); + services.Configure(ApiKeyDefaults.AuthenticationScheme, options => + { + options.Realm = "Elsa Workflows"; + options.KeyName = "ApiKey"; + }); + services.Configure(_ => { }); + services.Configure(_ => { }); + services.Configure(_ => { }); + + // Memory stores. + services + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore(); + + // User providers. + services + .AddScoped() + .AddScoped() + .AddScoped(); + + // Application providers. + services + .AddScoped() + .AddScoped(); + + // Role providers. + services + .AddScoped() + .AddScoped() + .AddScoped(); + + // Tenant resolution strategies. + services + .AddScoped() + .AddScoped(); + + // Services. + services + .AddScoped(UserStore) + .AddScoped(ApplicationStore) + .AddScoped(RoleStore) + .AddScoped(UserProvider) + .AddScoped(ApplicationProvider) + .AddScoped(RoleProvider) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()) + .AddScoped(sp => sp.GetRequiredService()) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddHttpContextAccessor() + ; + } +} From 92e4f9fc2bd09a493c8ff1db6ec0e4fb504a567a Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 15 Jan 2026 21:11:33 +0100 Subject: [PATCH 16/36] Set default signing key in `IdentityTokenOptions` for identity configuration --- src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs b/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs index 74e13f65fb..d03b496ee3 100644 --- a/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs +++ b/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs @@ -57,7 +57,7 @@ public class IdentityFeature : IFastEndpointsShellFeature public void ConfigureServices(IServiceCollection services) { // Configure options with defaults - services.Configure(_ => { }); + services.Configure(options => { options.SigningKey = "A really long signing key that should be replaced with something more secure."; }); services.Configure(ApiKeyDefaults.AuthenticationScheme, options => { options.Realm = "Elsa Workflows"; From 64866171a3c7b3d7004e9241ef5a0c4470f159f0 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 18 Jan 2026 21:15:10 +0100 Subject: [PATCH 17/36] Add service exclusion infrastructure for shell-specific contexts Introduce `IShellServiceExclusionProvider` and `IShellServiceExclusionRegistry` to manage excluded service types per-shell. Implement ASP.NET Core-specific providers for authentication and authorization to enable shell-specific configurations. Refactor `DefaultShellHost` to use the new exclusion registry for service inheritance filtering. --- src/apps/Elsa.ModularServer.Web/Program.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/apps/Elsa.ModularServer.Web/Program.cs b/src/apps/Elsa.ModularServer.Web/Program.cs index 87d16e4f4f..95a28518f1 100644 --- a/src/apps/Elsa.ModularServer.Web/Program.cs +++ b/src/apps/Elsa.ModularServer.Web/Program.cs @@ -3,15 +3,20 @@ var builder = WebApplication.CreateBuilder(args); var services = builder.Services; +// Configure CShells for multi-tenancy with ASP.NET Core integration +// This automatically registers shell-aware authentication and authorization providers builder.AddShells(); services.AddHealthChecks(); -services.AddAuthorization(); + +// Add minimal authentication and authorization services in root +// These are required for middleware validation - shells provide the actual configurations services.AddAuthentication(); +services.AddAuthorization(); var app = builder.Build(); app.MapHealthChecks("/"); -app.UseAuthorization(); -app.UseAuthentication(); -app.MapShells(); +app.MapShells(); // Sets HttpContext.RequestServices to shell's scoped provider +app.UseAuthentication(); // Runs after MapShells to access shell-specific auth schemes +app.UseAuthorization(); // Runs after MapShells to access shell-specific policies app.Run(); \ No newline at end of file From 3b6bab15d2a9c958611703ae879e2742538b7f06 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 18 Jan 2026 22:49:46 +0100 Subject: [PATCH 18/36] Refactor CShells authentication and authorization APIs Renamed and unified methods for shell authentication and authorization and added a new combined method `WithAuthenticationAndAuthorization`. Enhanced `AddShells` to automatically register a default configuration provider if none is specified. Updated usage in Elsa.ModularServer to utilize the new API. --- src/apps/Elsa.ModularServer.Web/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/apps/Elsa.ModularServer.Web/Program.cs b/src/apps/Elsa.ModularServer.Web/Program.cs index 95a28518f1..3d186eaf2d 100644 --- a/src/apps/Elsa.ModularServer.Web/Program.cs +++ b/src/apps/Elsa.ModularServer.Web/Program.cs @@ -1,3 +1,4 @@ +using CShells.AspNetCore.Configuration; using CShells.AspNetCore.Extensions; var builder = WebApplication.CreateBuilder(args); @@ -5,7 +6,7 @@ // Configure CShells for multi-tenancy with ASP.NET Core integration // This automatically registers shell-aware authentication and authorization providers -builder.AddShells(); +builder.AddShells(shells => shells.WithAuthenticationAndAuthorization()); services.AddHealthChecks(); // Add minimal authentication and authorization services in root From 14520aea3cb828d70182611dac266bd1462fd1ee Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 18 Jan 2026 23:25:18 +0100 Subject: [PATCH 19/36] Add Elsa-specific FastEndpoints configurator and feature Introduce `ElsaFastEndpointsConfigurator` to customize FastEndpoints serialization and value parsing for Elsa workflows. Register this functionality through the new `ElsaFastEndpointsFeature`, which integrates with the shell's dependency injection system using an `IFastEndpointsConfigurator` interface. --- .../ElsaFastEndpointsConfigurator.cs | 50 +++++++++++++++++++ .../ShellFeatures/ElsaFastEndpointsFeature.cs | 22 ++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs create mode 100644 src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs diff --git a/src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs b/src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs new file mode 100644 index 0000000000..947399a206 --- /dev/null +++ b/src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs @@ -0,0 +1,50 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; +using CShells.FastEndpoints.Contracts; +using Elsa.Workflows; +using FastEndpoints; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.FastEndpointConfigurators; + +/// +/// Configures FastEndpoints with Elsa-specific serialization options. +/// Uses the same serialization settings as . +/// +[UsedImplicitly] +public class ElsaFastEndpointsConfigurator : IFastEndpointsConfigurator +{ + /// + public void Configure(Config config) + { + config.Serializer.RequestDeserializer = DeserializeRequestAsync; + config.Serializer.ResponseSerializer = SerializeResponseAsync; + + config.Binding.ValueParserFor(s => + new(DateTimeOffset.TryParse(s.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result), result)); + } + + private static ValueTask DeserializeRequestAsync(HttpRequest httpRequest, Type modelType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken) + { + var serializer = httpRequest.HttpContext.RequestServices.GetRequiredService(); + var options = serializer.GetOptions(); + + return serializerContext == null + ? JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, options, cancellationToken) + : JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, serializerContext, cancellationToken); + } + + private static Task SerializeResponseAsync(HttpResponse httpResponse, object? dto, string contentType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken) + { + var serializer = httpResponse.HttpContext.RequestServices.GetRequiredService(); + var options = serializer.GetOptions(); + + httpResponse.ContentType = contentType; + return serializerContext == null + ? JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto?.GetType() ?? typeof(object), options, cancellationToken) + : JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto?.GetType() ?? typeof(object), serializerContext, cancellationToken); + } +} diff --git a/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs b/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs new file mode 100644 index 0000000000..84d864c36b --- /dev/null +++ b/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs @@ -0,0 +1,22 @@ +using CShells.FastEndpoints.Contracts; +using CShells.Features; +using Elsa.FastEndpointConfigurators; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.ShellFeatures; + +/// +/// Registers the Elsa-specific FastEndpoints configurator. +/// This feature should be enabled alongside the CShells FastEndpointsFeature to apply Elsa's serialization settings. +/// +[ShellFeature] +[UsedImplicitly] +public class ElsaFastEndpointsFeature : IShellFeature +{ + /// + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + } +} From 866d6618a41aa0b65cb1bdf19ed51d3676d4b5b1 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 18 Jan 2026 23:25:34 +0100 Subject: [PATCH 20/36] Update Workflow API feature dependency to `ElsaFastEndpoints` --- .../Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs b/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs index 9d33376072..de155669f4 100644 --- a/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs +++ b/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs @@ -23,7 +23,7 @@ namespace Elsa.Workflows.Api.ShellFeatures; Description = "Provides REST API endpoints for workflow management", DependsOn = [ - "FastEndpoints", + "ElsaFastEndpoints", "WorkflowInstances", "WorkflowManagement", "WorkflowRuntime", From 4ed87d5339fc55a921ad8f88726ac34371935ee7 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 18 Jan 2026 23:25:39 +0100 Subject: [PATCH 21/36] Pass `cancellationToken` to `ReadToEndAsync` in `PostEndpoint` for improved request handling. --- .../Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs index a7afdcfe47..caaef86c99 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs @@ -32,7 +32,7 @@ public override async Task HandleAsync(CancellationToken cancellationToken) if (HttpContext.Request.ContentType?.Contains("application/json") ?? false) { using var reader = new StreamReader(HttpContext.Request.Body); - var body = await reader.ReadToEndAsync(); + var body = await reader.ReadToEndAsync(cancellationToken); if (!string.IsNullOrWhiteSpace(body)) { From c9ffe29fad67fa67ae075d023d31e75577bb977b Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 18 Jan 2026 23:26:42 +0100 Subject: [PATCH 22/36] Add project references for CShells.AspNetCore and CShells.FastEndpoints.Abstractions --- src/common/Elsa.Api.Common/Elsa.Api.Common.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj b/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj index 8288f16c23..b1d3a9df7b 100644 --- a/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj +++ b/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj @@ -9,12 +9,16 @@ + + + + From 5c35c6804e62f2e056c4ffc9b1428e3bada48221 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 18 Jan 2026 23:40:52 +0100 Subject: [PATCH 23/36] Update CShells package versions to `0.0.6-preview.30` and add `CShells.FastEndpoints.Abstractions` --- Directory.Packages.props | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 29087b94e8..b32a41fa11 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -100,10 +100,11 @@ - - - - + + + + + From d3f52ff102cdade628c7e5d3f7545b4e147ec675 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 19 Jan 2026 08:54:11 +0100 Subject: [PATCH 24/36] Configures shell routing and features Enables path routing for shells to allow proper routing within each shell. Configures the ElsaFastEndpoints feature to depend on the FastEndpoints feature. This ensures that FastEndpoints is properly configured before Elsa's FastEndpoints configurations are applied. Registers activity types within the WorkflowManagementFeature. This ensures activities are available for workflow construction and execution. --- src/apps/Elsa.ModularServer.Web/Program.cs | 4 ++- .../ShellFeatures/ElsaFastEndpointsFeature.cs | 2 +- .../WorkflowManagementFeature.cs | 29 +++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/apps/Elsa.ModularServer.Web/Program.cs b/src/apps/Elsa.ModularServer.Web/Program.cs index 3d186eaf2d..a5251a00ff 100644 --- a/src/apps/Elsa.ModularServer.Web/Program.cs +++ b/src/apps/Elsa.ModularServer.Web/Program.cs @@ -6,7 +6,9 @@ // Configure CShells for multi-tenancy with ASP.NET Core integration // This automatically registers shell-aware authentication and authorization providers -builder.AddShells(shells => shells.WithAuthenticationAndAuthorization()); +builder.AddShells(shells => shells + .WithWebRouting(options => options.EnablePathRouting = true) + .WithAuthenticationAndAuthorization()); services.AddHealthChecks(); // Add minimal authentication and authorization services in root diff --git a/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs b/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs index 84d864c36b..3782b9ad23 100644 --- a/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs +++ b/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs @@ -10,7 +10,7 @@ namespace Elsa.ShellFeatures; /// Registers the Elsa-specific FastEndpoints configurator. /// This feature should be enabled alongside the CShells FastEndpointsFeature to apply Elsa's serialization settings. /// -[ShellFeature] +[ShellFeature(DependsOn = ["FastEndpoints"])] [UsedImplicitly] public class ElsaFastEndpointsFeature : IShellFeature { diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs index faf8144d6a..bee905ecd0 100644 --- a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs @@ -129,10 +129,39 @@ public void ConfigureServices(IServiceCollection services) .AddNotificationHandler() ; + AddActivitiesFrom(); + AddActivitiesFrom(); + + services.Configure(options => { + foreach (var activityType in ActivityTypes.Distinct()) + options.ActivityTypes.Add(activityType); + foreach (var descriptor in VariableDescriptors.DistinctBy(x => x.Type)) options.VariableDescriptors.Add(descriptor); }); } + + /// + /// A set of activity types to make available to the system. + /// + private HashSet ActivityTypes { get; } = []; + + /// + /// Adds the specified activity types to the system. + /// + private WorkflowManagementFeature AddActivities(IEnumerable activityTypes) + { + ActivityTypes.AddRange(activityTypes); + return this; + } + + private WorkflowManagementFeature AddActivitiesFrom() + { + var activityTypes = typeof(TMarker).Assembly.GetExportedTypes() + .Where(x => typeof(IActivity).IsAssignableFrom(x) && x is { IsAbstract: false, IsInterface: false, IsGenericType: false }) + .ToList(); + return AddActivities(activityTypes); + } } \ No newline at end of file From d71e22d8b0f3db6b893d2dd3ba43114451780ab6 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 19 Jan 2026 20:36:56 +0100 Subject: [PATCH 25/36] Add shell lifecycle management and notification handlers Introduced interfaces and handlers for shell activation (`IShellActivatedHandler`) and deactivation (`IShellDeactivatingHandler`) to manage shell lifecycles. Added `ShellStartupHostedService` to coordinate shell activation on startup and deactivation on shutdown. Updated notification system to support new shell lifecycle events and renamed existing notification records for consistency. --- .../ShellFeatures/MultitenancyFeature.cs | 5 ++++- .../ShellHandlers/ActivateShellTenants.cs | 19 +++++++++++++++++++ .../WorkflowManagementFeature.cs | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs diff --git a/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs index b02cb5ae7e..7e43f4e5e6 100644 --- a/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs +++ b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs @@ -1,8 +1,10 @@ using CShells.Features; +using CShells.Hosting; using Elsa.Common.Multitenancy; using Elsa.Common.Multitenancy.EventHandlers; using Elsa.Common.Multitenancy.HostedServices; using Elsa.Common.RecurringTasks; +using Elsa.Common.ShellHandlers; using Microsoft.Extensions.DependencyInjection; namespace Elsa.Common.ShellFeatures; @@ -32,7 +34,8 @@ public void ConfigureServices(IServiceCollection services) .AddScoped() .AddScoped(_tenantsProviderFactory) - .AddHostedService() + .AddSingleton() + .AddSingleton() ; } } \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs b/src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs new file mode 100644 index 0000000000..39f0fd0f77 --- /dev/null +++ b/src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs @@ -0,0 +1,19 @@ +using CShells.Hosting; +using Elsa.Common.Multitenancy; +using JetBrains.Annotations; + +namespace Elsa.Common.ShellHandlers; + +[UsedImplicitly] +public class ActivateShellTenants(ITenantService tenantService) : IShellActivatedHandler, IShellDeactivatingHandler +{ + public Task OnActivatedAsync(CancellationToken cancellationToken = default) + { + return tenantService.ActivateTenantsAsync(cancellationToken); + } + + public Task OnDeactivatingAsync(CancellationToken cancellationToken = default) + { + return tenantService.DeactivateTenantsAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs index bee905ecd0..f5476f2c85 100644 --- a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs @@ -94,6 +94,7 @@ public void ConfigureServices(IServiceCollection services) .AddScoped() .AddScoped() .AddScoped() + .AddScoped() .AddScoped() .AddScoped() .AddScoped() From 855bfb0dec08e5a617fb00d574d99c28a596df24 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Tue, 20 Jan 2026 21:08:11 +0100 Subject: [PATCH 26/36] Introduces EF Core persistence layer Adds base classes and implementations for EF Core persistence, including database provider configuration and shell feature integration. This change introduces a generic approach to configuring EF Core persistence for various Elsa modules, promoting code reuse and simplifying the process of supporting different database providers. It includes: - Base classes for database provider configurators and shell features. - Implementations for Sqlite, SQL Server, MySql, PostgreSql, and Oracle. - Shell features for Alterations, Identity, Labels, Management (Workflow Definitions and Instances), Runtime, and Tenants modules. --- .../DatabaseProviderConfigurator.cs | 58 ++++++++++++ .../DatabaseProviderShellFeature.cs | 57 +++++++++++ .../PersistenceShellFeatureBase.cs | 94 +++++++++++++++++++ .../MySqlProviderConfigurator.cs | 32 +++++++ ...MySqlAlterationsPersistenceShellFeature.cs | 25 +++++ .../MySqlIdentityPersistenceShellFeature.cs | 25 +++++ .../MySqlLabelPersistenceShellFeature.cs | 25 +++++ ...rkflowDefinitionPersistenceShellFeature.cs | 25 +++++ ...WorkflowInstancePersistenceShellFeature.cs | 25 +++++ ...lWorkflowRuntimePersistenceShellFeature.cs | 25 +++++ .../MySqlTenantPersistenceShellFeature.cs | 25 +++++ .../OracleProviderConfigurator.cs | 34 +++++++ ...racleAlterationsPersistenceShellFeature.cs | 25 +++++ .../OracleIdentityPersistenceShellFeature.cs | 25 +++++ .../OracleLabelPersistenceShellFeature.cs | 25 +++++ ...rkflowDefinitionPersistenceShellFeature.cs | 25 +++++ ...WorkflowInstancePersistenceShellFeature.cs | 25 +++++ ...eWorkflowRuntimePersistenceShellFeature.cs | 25 +++++ .../OracleTenantPersistenceShellFeature.cs | 25 +++++ .../PostgreSqlProviderConfigurator.cs | 32 +++++++ ...reSqlAlterationsPersistenceShellFeature.cs | 25 +++++ ...stgreSqlIdentityPersistenceShellFeature.cs | 25 +++++ .../PostgreSqlLabelPersistenceShellFeature.cs | 25 +++++ ...rkflowDefinitionPersistenceShellFeature.cs | 25 +++++ ...WorkflowInstancePersistenceShellFeature.cs | 25 +++++ ...lWorkflowRuntimePersistenceShellFeature.cs | 25 +++++ ...PostgreSqlTenantPersistenceShellFeature.cs | 25 +++++ ...erverAlterationsPersistenceShellFeature.cs | 25 +++++ ...qlServerIdentityPersistenceShellFeature.cs | 25 +++++ .../SqlServerLabelPersistenceShellFeature.cs | 25 +++++ ...rkflowDefinitionPersistenceShellFeature.cs | 25 +++++ ...WorkflowInstancePersistenceShellFeature.cs | 25 +++++ ...rWorkflowRuntimePersistenceShellFeature.cs | 25 +++++ .../SqlServerTenantPersistenceShellFeature.cs | 25 +++++ .../SqlServerProviderConfigurator.cs | 32 +++++++ ...qliteAlterationsPersistenceShellFeature.cs | 25 +++++ .../SqliteIdentityPersistenceShellFeature.cs | 25 +++++ .../SqliteLabelPersistenceShellFeature.cs | 25 +++++ ...rkflowDefinitionPersistenceShellFeature.cs | 25 +++++ ...WorkflowInstancePersistenceShellFeature.cs | 25 +++++ ...eWorkflowRuntimePersistenceShellFeature.cs | 25 +++++ .../SqliteTenantPersistenceShellFeature.cs | 25 +++++ .../SqliteProviderConfigurator.cs | 32 +++++++ .../Modules/Alterations/ShellFeature.cs | 23 +++++ .../Modules/Identity/ShellFeature.cs | 24 +++++ .../Modules/Labels/ShellFeature.cs | 23 +++++ ...rkflowDefinitionPersistenceShellFeature.cs | 22 +++++ ...WorkflowInstancePersistenceShellFeature.cs | 22 +++++ .../WorkflowRuntimePersistenceShellFeature.cs | 28 ++++++ .../Modules/Tenants/ShellFeature.cs | 22 +++++ 50 files changed, 1410 insertions(+) create mode 100644 src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs diff --git a/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs new file mode 100644 index 0000000000..5e744747bb --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore; + +/// +/// Base class for database provider configurations that can be applied to any persistence feature. +/// +public abstract class DatabaseProviderConfigurator +{ + /// + /// Gets the assembly containing migrations for this provider. + /// + protected Assembly MigrationsAssembly { get; } + + /// + /// Gets or sets the connection string to use. + /// + public Func ConnectionString { get; set; } + = _ => throw new InvalidOperationException("Connection string is required."); + + /// + /// Gets or sets additional options to configure the database context. + /// + public ElsaDbContextOptions? DbContextOptions { get; set; } + + /// + /// Gets or sets a callback to configure provider-specific options. + /// + public Action? ProviderOptions { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The assembly containing migrations for this provider. + protected DatabaseProviderConfigurator(Assembly migrationsAssembly) + { + MigrationsAssembly = migrationsAssembly; + } + + /// + /// Configures the database provider for the specified . + /// + protected abstract DbContextOptionsBuilder ConfigureProvider( + DbContextOptionsBuilder builder, + Assembly assembly, + string connectionString, + ElsaDbContextOptions? options, + Action? configure); + + /// + /// Gets the configuration delegate. + /// + public Action GetDbContextOptionsBuilder() + { + return (sp, db) => ConfigureProvider(db, MigrationsAssembly, ConnectionString(sp), DbContextOptions, ProviderOptions); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs new file mode 100644 index 0000000000..da8adc98e3 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs @@ -0,0 +1,57 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore; + +/// +/// A generic shell feature that applies a database provider configuration to a persistence feature. +/// +public class DatabaseProviderShellFeature + : PersistenceShellFeatureBase, TDbContext> + where TBaseFeature : PersistenceShellFeatureBase + where TDbContext : ElsaDbContextBase +{ + private readonly DatabaseProviderConfigurator _providerConfigurator; + + /// + /// Initializes a new instance of the class. + /// + /// The database provider configurator to use. + protected DatabaseProviderShellFeature(DatabaseProviderConfigurator providerConfigurator) + { + _providerConfigurator = providerConfigurator; + } + + /// + /// Gets or sets the connection string to use. + /// + public Func ConnectionString + { + get => _providerConfigurator.ConnectionString; + set => _providerConfigurator.ConnectionString = value; + } + + /// + /// Gets or sets additional options to configure the database context. + /// + public ElsaDbContextOptions? DbContextOptions + { + get => _providerConfigurator.DbContextOptions; + set => _providerConfigurator.DbContextOptions = value; + } + + /// + /// Gets or sets a callback to configure provider-specific options. + /// + public Action? ProviderOptions + { + get => _providerConfigurator.ProviderOptions; + set => _providerConfigurator.ProviderOptions = value; + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + DbContextOptionsBuilder = _providerConfigurator.GetDbContextOptionsBuilder(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs b/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs new file mode 100644 index 0000000000..f0c1ed50f5 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs @@ -0,0 +1,94 @@ +using CShells.Features; +using Elsa.Common.Entities; +using Elsa.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.DependencyInjection; + +// ReSharper disable once CheckNamespace +namespace Elsa.Persistence.EFCore; + +public abstract class PersistenceShellFeatureBase : IShellFeature + where TDbContext : ElsaDbContextBase +{ + /// + /// Gets or sets a value indicating whether to use context pooling. + /// + public virtual bool UseContextPooling { get; set; } + + /// + /// Gets or sets a value indicating whether to run migrations. + /// + public virtual bool RunMigrations { get; set; } = true; + + /// + /// Gets or sets the lifetime of the . Defaults to . + /// + public ServiceLifetime DbContextFactoryLifetime { get; set; } = ServiceLifetime.Scoped; + + /// + /// Gets or sets the callback used to configure the . + /// + public virtual Action DbContextOptionsBuilder { get; set; } = null!; + + public void ConfigureServices(IServiceCollection services) + { + if (DbContextOptionsBuilder == null) + throw new InvalidOperationException("The DbContextOptionsBuilder must be configured."); + + Action setup = (sp, opts) => + { + opts.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); + DbContextOptionsBuilder(sp, opts); + }; + + if (UseContextPooling) + services.AddPooledDbContextFactory(setup); + else + services.AddDbContextFactory(setup, DbContextFactoryLifetime); + + services.Decorate, TenantAwareDbContextFactory>(); + + services.Configure(options => + { + options.RunMigrations[typeof(TDbContext)] = RunMigrations; + }); + + OnConfiguring(services); + } + + protected virtual void OnConfiguring(IServiceCollection services) + { + } + + protected virtual void ConfigureMigrations(IServiceCollection services) + { + services.AddStartupTask>(); + } + + /// + /// Adds a store to the service collection. + /// + /// The type of the entity. + /// The type of the store. + protected void AddStore(IServiceCollection services) where TEntity : class, new() where TStore : class + { + services + .AddScoped>() + .AddScoped() + ; + } + + /// + /// Adds an entity store to the service collection. + /// + /// The type of the entity. + /// The type of the store. + protected void AddEntityStore(IServiceCollection services) where TEntity : Entity, new() where TStore : class + { + services + .AddScoped>() + .AddScoped() + ; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs new file mode 100644 index 0000000000..e37eac5640 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using Elsa.Persistence.EFCore.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.MySql; + +/// +/// Configures MySQL as the database provider. +/// +public class MySqlProviderConfigurator : DatabaseProviderConfigurator +{ + /// + /// Initializes a new instance of the class. + /// + /// The assembly containing MySQL migrations. + public MySqlProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) + { + ConnectionString = _ => throw new InvalidOperationException("Connection string is required for MySQL."); + } + + /// + protected override DbContextOptionsBuilder ConfigureProvider( + DbContextOptionsBuilder builder, + Assembly assembly, + string connectionString, + ElsaDbContextOptions? options, + Action? configure) + { + return builder.UseElsaMySql(assembly, connectionString, options, configure: configure); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs new file mode 100644 index 0000000000..1314aae4b4 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Alterations Persistence", + Description = "Provides MySql persistence for workflow alterations")] +[UsedImplicitly] +public class MySqlAlterationsPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public MySqlAlterationsPersistenceShellFeature() + : base(new MySqlProviderConfigurator(typeof(MySqlAlterationsPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs new file mode 100644 index 0000000000..fa00ccb963 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Identity Persistence", + Description = "Provides MySql persistence for identity management")] +[UsedImplicitly] +public class MySqlIdentityPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public MySqlIdentityPersistenceShellFeature() + : base(new MySqlProviderConfigurator(typeof(MySqlIdentityPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs new file mode 100644 index 0000000000..4dff1edae1 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Label Persistence", + Description = "Provides MySql persistence for label management")] +[UsedImplicitly] +public class MySqlLabelPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public MySqlLabelPersistenceShellFeature() + : base(new MySqlProviderConfigurator(typeof(MySqlLabelPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..961189201f --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Management; + +/// +/// Configures the management feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Definition Persistence", + Description = "Provides MySql persistence for workflow definitions")] +[UsedImplicitly] +public class MySqlWorkflowDefinitionPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public MySqlWorkflowDefinitionPersistenceShellFeature() + : base(new MySqlProviderConfigurator(typeof(MySqlWorkflowDefinitionPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..b195a4526b --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Management; + +/// +/// Configures the management feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Instance Persistence", + Description = "Provides MySql persistence for workflow instances")] +[UsedImplicitly] +public class MySqlWorkflowInstancePersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public MySqlWorkflowInstancePersistenceShellFeature() + : base(new MySqlProviderConfigurator(typeof(MySqlWorkflowInstancePersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..b5bde8e69d --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Runtime Persistence", + Description = "Provides MySql persistence for workflow runtime")] +[UsedImplicitly] +public class MySqlWorkflowRuntimePersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public MySqlWorkflowRuntimePersistenceShellFeature() + : base(new MySqlProviderConfigurator(typeof(MySqlWorkflowRuntimePersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs new file mode 100644 index 0000000000..1900e46a16 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Tenant Persistence", + Description = "Provides MySql persistence for tenant management")] +[UsedImplicitly] +public class MySqlTenantPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public MySqlTenantPersistenceShellFeature() + : base(new MySqlProviderConfigurator(typeof(MySqlTenantPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs new file mode 100644 index 0000000000..6e186c4633 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using Elsa.Persistence.EFCore.Extensions; +using Microsoft.EntityFrameworkCore; +using Oracle.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Oracle; + +/// +/// Configures Oracle as the database provider. +/// +public class OracleProviderConfigurator : DatabaseProviderConfigurator +{ + /// + /// Initializes a new instance of the class. + /// + /// The assembly containing Oracle migrations. + public OracleProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) + { + ConnectionString = _ => throw new InvalidOperationException("Connection string is required for Oracle."); + } + + /// + protected override DbContextOptionsBuilder ConfigureProvider( + DbContextOptionsBuilder builder, + Assembly assembly, + string connectionString, + ElsaDbContextOptions? options, + Action? configure) + { + DbContextOptions ??= new(); + DbContextOptions.Configure(); + return builder.UseElsaOracle(assembly, connectionString, DbContextOptions, configure); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs new file mode 100644 index 0000000000..22c3b69b12 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Oracle.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Alterations Persistence", + Description = "Provides Oracle persistence for workflow alterations")] +[UsedImplicitly] +public class OracleAlterationsPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public OracleAlterationsPersistenceShellFeature() + : base(new OracleProviderConfigurator(typeof(OracleAlterationsPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs new file mode 100644 index 0000000000..702a33e3db --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Oracle.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Identity Persistence", + Description = "Provides Oracle persistence for identity management")] +[UsedImplicitly] +public class OracleIdentityPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public OracleIdentityPersistenceShellFeature() + : base(new OracleProviderConfigurator(typeof(OracleIdentityPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs new file mode 100644 index 0000000000..b6fc65b07b --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Oracle.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Label Persistence", + Description = "Provides Oracle persistence for label management")] +[UsedImplicitly] +public class OracleLabelPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public OracleLabelPersistenceShellFeature() + : base(new OracleProviderConfigurator(typeof(OracleLabelPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..6040dda87d --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Oracle.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Management; + +/// +/// Configures the management feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Definition Persistence", + Description = "Provides Oracle persistence for workflow definitions")] +[UsedImplicitly] +public class OracleWorkflowDefinitionPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public OracleWorkflowDefinitionPersistenceShellFeature() + : base(new OracleProviderConfigurator(typeof(OracleWorkflowDefinitionPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..92c1426388 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Oracle.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Management; + +/// +/// Configures the management feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Instance Persistence", + Description = "Provides Oracle persistence for workflow instances")] +[UsedImplicitly] +public class OracleWorkflowInstancePersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public OracleWorkflowInstancePersistenceShellFeature() + : base(new OracleProviderConfigurator(typeof(OracleWorkflowInstancePersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..72ba04548f --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Oracle.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Runtime Persistence", + Description = "Provides Oracle persistence for workflow runtime")] +[UsedImplicitly] +public class OracleWorkflowRuntimePersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public OracleWorkflowRuntimePersistenceShellFeature() + : base(new OracleProviderConfigurator(typeof(OracleWorkflowRuntimePersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs new file mode 100644 index 0000000000..a139c5e09d --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Oracle.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Tenant Persistence", + Description = "Provides Oracle persistence for tenant management")] +[UsedImplicitly] +public class OracleTenantPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public OracleTenantPersistenceShellFeature() + : base(new OracleProviderConfigurator(typeof(OracleTenantPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs new file mode 100644 index 0000000000..c1675b2844 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using Elsa.Persistence.EFCore.Extensions; +using Microsoft.EntityFrameworkCore; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + +namespace Elsa.Persistence.EFCore.PostgreSql; + +/// +/// Configures PostgreSQL as the database provider. +/// +public class PostgreSqlProviderConfigurator : DatabaseProviderConfigurator +{ + /// + /// Initializes a new instance of the class. + /// + /// The assembly containing PostgreSQL migrations. + public PostgreSqlProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) + { + ConnectionString = _ => throw new InvalidOperationException("Connection string is required for PostgreSQL."); + } + + /// + protected override DbContextOptionsBuilder ConfigureProvider( + DbContextOptionsBuilder builder, + Assembly assembly, + string connectionString, + ElsaDbContextOptions? options, + Action? configure) + { + return builder.UseElsaPostgreSql(assembly, connectionString, options, configure); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs new file mode 100644 index 0000000000..5630b36bca --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Alterations Persistence", + Description = "Provides PostgreSql persistence for workflow alterations")] +[UsedImplicitly] +public class PostgreSqlAlterationsPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public PostgreSqlAlterationsPersistenceShellFeature() + : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlAlterationsPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs new file mode 100644 index 0000000000..5836699895 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Identity Persistence", + Description = "Provides PostgreSql persistence for identity management")] +[UsedImplicitly] +public class PostgreSqlIdentityPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public PostgreSqlIdentityPersistenceShellFeature() + : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlIdentityPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs new file mode 100644 index 0000000000..7418ec6d6f --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Label Persistence", + Description = "Provides PostgreSql persistence for label management")] +[UsedImplicitly] +public class PostgreSqlLabelPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public PostgreSqlLabelPersistenceShellFeature() + : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlLabelPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..a75d1deec1 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Management; + +/// +/// Configures the management feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Definition Persistence", + Description = "Provides PostgreSql persistence for workflow definitions")] +[UsedImplicitly] +public class PostgreSqlWorkflowDefinitionPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public PostgreSqlWorkflowDefinitionPersistenceShellFeature() + : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlWorkflowDefinitionPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..46e66699e9 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Management; + +/// +/// Configures the management feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Instance Persistence", + Description = "Provides PostgreSql persistence for workflow instances")] +[UsedImplicitly] +public class PostgreSqlWorkflowInstancePersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public PostgreSqlWorkflowInstancePersistenceShellFeature() + : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlWorkflowInstancePersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..d930862e4e --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Runtime Persistence", + Description = "Provides PostgreSql persistence for workflow runtime")] +[UsedImplicitly] +public class PostgreSqlWorkflowRuntimePersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public PostgreSqlWorkflowRuntimePersistenceShellFeature() + : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlWorkflowRuntimePersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs new file mode 100644 index 0000000000..f34c3c286b --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Tenant Persistence", + Description = "Provides PostgreSql persistence for tenant management")] +[UsedImplicitly] +public class PostgreSqlTenantPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public PostgreSqlTenantPersistenceShellFeature() + : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlTenantPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs new file mode 100644 index 0000000000..43a96ea637 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Alterations Persistence", + Description = "Provides SqlServer persistence for workflow alterations")] +[UsedImplicitly] +public class SqlServerAlterationsPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqlServerAlterationsPersistenceShellFeature() + : base(new SqlServerProviderConfigurator(typeof(SqlServerAlterationsPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs new file mode 100644 index 0000000000..92cb7b3bdc --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Identity Persistence", + Description = "Provides SqlServer persistence for identity management")] +[UsedImplicitly] +public class SqlServerIdentityPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqlServerIdentityPersistenceShellFeature() + : base(new SqlServerProviderConfigurator(typeof(SqlServerIdentityPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs new file mode 100644 index 0000000000..f5cb57b0c2 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Label Persistence", + Description = "Provides SqlServer persistence for label management")] +[UsedImplicitly] +public class SqlServerLabelPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqlServerLabelPersistenceShellFeature() + : base(new SqlServerProviderConfigurator(typeof(SqlServerLabelPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..61e28bc0c5 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Management; + +/// +/// Configures the management feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Workflow Definition Persistence", + Description = "Provides SqlServer persistence for workflow definitions")] +[UsedImplicitly] +public class SqlServerWorkflowDefinitionPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqlServerWorkflowDefinitionPersistenceShellFeature() + : base(new SqlServerProviderConfigurator(typeof(SqlServerWorkflowDefinitionPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..a2bdc498f7 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Management; + +/// +/// Configures the management feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Workflow Instance Persistence", + Description = "Provides SqlServer persistence for workflow instances")] +[UsedImplicitly] +public class SqlServerWorkflowInstancePersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqlServerWorkflowInstancePersistenceShellFeature() + : base(new SqlServerProviderConfigurator(typeof(SqlServerWorkflowInstancePersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..b49fc708cd --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Workflow Runtime Persistence", + Description = "Provides SqlServer persistence for workflow runtime")] +[UsedImplicitly] +public class SqlServerWorkflowRuntimePersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqlServerWorkflowRuntimePersistenceShellFeature() + : base(new SqlServerProviderConfigurator(typeof(SqlServerWorkflowRuntimePersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs new file mode 100644 index 0000000000..7740bab216 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Tenant Persistence", + Description = "Provides SqlServer persistence for tenant management")] +[UsedImplicitly] +public class SqlServerTenantPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqlServerTenantPersistenceShellFeature() + : base(new SqlServerProviderConfigurator(typeof(SqlServerTenantPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs new file mode 100644 index 0000000000..ad9fde47fa --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using Elsa.Persistence.EFCore.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.SqlServer; + +/// +/// Configures SQL Server as the database provider. +/// +public class SqlServerProviderConfigurator : DatabaseProviderConfigurator +{ + /// + /// Initializes a new instance of the class. + /// + /// The assembly containing SQL Server migrations. + public SqlServerProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) + { + ConnectionString = _ => throw new InvalidOperationException("Connection string is required for SQL Server."); + } + + /// + protected override DbContextOptionsBuilder ConfigureProvider( + DbContextOptionsBuilder builder, + Assembly assembly, + string connectionString, + ElsaDbContextOptions? options, + Action? configure) + { + return builder.UseElsaSqlServer(assembly, connectionString, options, configure); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs new file mode 100644 index 0000000000..2bbc4258e2 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Alterations Persistence", + Description = "Provides Sqlite persistence for workflow alterations")] +[UsedImplicitly] +public class SqliteAlterationsPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqliteAlterationsPersistenceShellFeature() + : base(new SqliteProviderConfigurator(typeof(SqliteAlterationsPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs new file mode 100644 index 0000000000..7b12feef7b --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Identity Persistence", + Description = "Provides Sqlite persistence for identity management")] +[UsedImplicitly] +public class SqliteIdentityPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqliteIdentityPersistenceShellFeature() + : base(new SqliteProviderConfigurator(typeof(SqliteIdentityPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs new file mode 100644 index 0000000000..9238593b57 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Label Persistence", + Description = "Provides Sqlite persistence for label management")] +[UsedImplicitly] +public class SqliteLabelPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqliteLabelPersistenceShellFeature() + : base(new SqliteProviderConfigurator(typeof(SqliteLabelPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..df9dd3dced --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; + +/// +/// Configures the management feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Definition Persistence", + Description = "Provides Sqlite persistence for workflow definitions")] +[UsedImplicitly] +public class SqliteWorkflowDefinitionPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqliteWorkflowDefinitionPersistenceShellFeature() + : base(new SqliteProviderConfigurator(typeof(SqliteWorkflowDefinitionPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..77c9361927 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; + +/// +/// Configures the management feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Instance Persistence", + Description = "Provides Sqlite persistence for workflow instances")] +[UsedImplicitly] +public class SqliteWorkflowInstancePersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqliteWorkflowInstancePersistenceShellFeature() + : base(new SqliteProviderConfigurator(typeof(SqliteWorkflowInstancePersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..52451d0b73 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Runtime Persistence", + Description = "Provides Sqlite persistence for workflow runtime")] +[UsedImplicitly] +public class SqliteWorkflowRuntimePersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqliteWorkflowRuntimePersistenceShellFeature() + : base(new SqliteProviderConfigurator(typeof(SqliteWorkflowRuntimePersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs new file mode 100644 index 0000000000..413b207ae1 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Tenant Persistence", + Description = "Provides Sqlite persistence for tenant management")] +[UsedImplicitly] +public class SqliteTenantPersistenceShellFeature + : DatabaseProviderShellFeature +{ + /// + /// Initializes a new instance of the class. + /// + public SqliteTenantPersistenceShellFeature() + : base(new SqliteProviderConfigurator(typeof(SqliteTenantPersistenceShellFeature).Assembly)) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs new file mode 100644 index 0000000000..5e7ac58afa --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using Elsa.Persistence.EFCore.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Sqlite; + +/// +/// Configures SQLite as the database provider. +/// +public class SqliteProviderConfigurator : DatabaseProviderConfigurator +{ + /// + /// Initializes a new instance of the class. + /// + /// The assembly containing SQLite migrations. + public SqliteProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) + { + ConnectionString = _ => "Data Source=elsa.sqlite.db;Cache=Shared;"; + } + + /// + protected override DbContextOptionsBuilder ConfigureProvider( + DbContextOptionsBuilder builder, + Assembly assembly, + string connectionString, + ElsaDbContextOptions? options, + Action? configure) + { + return builder.UseElsaSqlite(assembly, connectionString, options, configure); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs new file mode 100644 index 0000000000..cca6269129 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs @@ -0,0 +1,23 @@ +using CShells.Features; +using Elsa.Alterations.Core.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Alterations; + +/// +/// Configures the alterations feature with an Entity Framework Core persistence provider. +/// +[ShellFeature( + DisplayName = "EF Core Alterations Persistence", + Description = "Provides Entity Framework Core persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class EFCoreAlterationsPersistenceShellFeature : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + AddEntityStore(services); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs new file mode 100644 index 0000000000..1a0060c44a --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs @@ -0,0 +1,24 @@ +using CShells.Features; +using Elsa.Identity.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Identity; + +/// +/// Configures the identity feature with Entity Framework Core persistence providers. +/// +[ShellFeature( + DisplayName = "EF Core Identity Persistence", + Description = "Provides Entity Framework Core persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class EFCoreIdentityPersistenceShellFeature : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + AddEntityStore(services); + AddEntityStore(services); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs new file mode 100644 index 0000000000..9b1a6888b9 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs @@ -0,0 +1,23 @@ +using CShells.Features; +using Elsa.Labels.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Labels; + +/// +/// Configures the labels feature with an Entity Framework Core persistence provider. +/// +[ShellFeature( + DisplayName = "EF Core Label Persistence", + Description = "Provides Entity Framework Core persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class EFCoreLabelPersistenceShellFeature : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + AddEntityStore(services); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..d6171caf4d --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,22 @@ +using CShells.Features; +using Elsa.Workflows.Management.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Management; + +/// +/// Configures the workflow definitions feature with an Entity Framework Core persistence provider. +/// +[ShellFeature( + DisplayName = "EF Core Workflow Definition Persistence", + Description = "Provides Entity Framework Core persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class EFCoreWorkflowDefinitionPersistenceShellFeature : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..a03ca36e00 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,22 @@ +using CShells.Features; +using Elsa.Workflows.Management.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Management; + +/// +/// Configures the workflow instances feature with an Entity Framework Core persistence provider. +/// +[ShellFeature( + DisplayName = "EF Core Workflow Instance Persistence", + Description = "Provides Entity Framework Core persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class EFCoreWorkflowInstancePersistenceShellFeature : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..4f761dd6d1 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,28 @@ +using CShells.Features; +using Elsa.KeyValues.Entities; +using Elsa.Workflows.Runtime.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Runtime; + +/// +/// Configures the workflow runtime feature with Entity Framework Core persistence providers. +/// +[ShellFeature( + DisplayName = "EF Core Workflow Runtime Persistence", + Description = "Provides Entity Framework Core persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class EFCoreWorkflowRuntimePersistenceShellFeature : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + AddEntityStore(services); + AddStore(services); + AddStore(services); + AddEntityStore(services); + AddEntityStore(services); + AddStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs new file mode 100644 index 0000000000..d8abd6ce5a --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs @@ -0,0 +1,22 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Tenants; + +/// +/// Configures the tenant management feature with an Entity Framework Core persistence provider. +/// +[ShellFeature( + DisplayName = "EF Core Tenant Management Persistence", + Description = "Provides Entity Framework Core persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class EFCoreTenantManagementShellFeature : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + AddEntityStore(services); + } +} From d0e68c192c2de3dd1c41a4e48869c1078b5c3d70 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 21 Jan 2026 12:25:24 +0100 Subject: [PATCH 27/36] Add comprehensive feature configuration validation system Introduce a feature configuration system with support for binding, auto-configuration, and validation using DataAnnotations, FluentValidation, and composite patterns. Includes new validators, binding logic, and extensions to simplify configuration tasks while ensuring robustness and flexibility. --- .../appsettings.Example.json | 76 +++++++++++++++++++ .../Elsa.ModularServer.Web/appsettings.json | 51 +++++++++---- .../DatabaseProviderConfigurator.cs | 5 +- .../DatabaseProviderShellFeature.cs | 7 +- .../MySqlProviderConfigurator.cs | 1 - .../OracleProviderConfigurator.cs | 1 - .../PostgreSqlProviderConfigurator.cs | 1 - .../SqlServerProviderConfigurator.cs | 1 - .../SqliteProviderConfigurator.cs | 1 - 9 files changed, 120 insertions(+), 24 deletions(-) create mode 100644 src/apps/Elsa.ModularServer.Web/appsettings.Example.json diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.Example.json b/src/apps/Elsa.ModularServer.Web/appsettings.Example.json new file mode 100644 index 0000000000..86ca822c4c --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/appsettings.Example.json @@ -0,0 +1,76 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "CShells": { + "Shells": [ + { + "Name": "Default", + "Properties": { + "WebRouting": { + "Path": "" + } + }, + "Settings": { + "FastEndpoints": { + "GlobalRoutePrefix": "elsa/api" + }, + "SqliteWorkflowDefinitionPersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + }, + "SqliteWorkflowInstancePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared" + }, + "SqliteWorkflowRuntimePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared" + } + }, + "Features": [ + "Elsa", + "WorkflowsApi", + "Identity", + "DefaultAuthentication", + "SqliteWorkflowDefinitionPersistence", + "SqliteWorkflowInstancePersistence", + "SqliteWorkflowRuntimePersistence" + ] + }, + { + "Name": "Tenant1", + "Properties": { + "WebRouting": { + "Path": "tenant1" + } + }, + "Settings": { + "FastEndpoints": { + "GlobalRoutePrefix": "api" + }, + "SqlServerWorkflowDefinitionPersistence": { + "ConnectionString": "${ConnectionStrings:Tenant1}" + }, + "SqlServerWorkflowInstancePersistence": { + "ConnectionString": "${ConnectionStrings:Tenant1}" + } + }, + "Features": [ + "Elsa", + "WorkflowsApi", + "SqlServerWorkflowDefinitionPersistence", + "SqlServerWorkflowInstancePersistence" + ] + } + ] + }, + "ConnectionStrings": { + "Tenant1": "Server=localhost;Database=ElsaTenant1;Integrated Security=true;TrustServerCertificate=true" + } +} diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json index d01934f565..acf3367788 100644 --- a/src/apps/Elsa.ModularServer.Web/appsettings.json +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -7,19 +7,42 @@ }, "AllowedHosts": "*", "CShells": { - "Shells": [{ - "Name": "Default", - "Properties": { - "WebRouting": { - "Path": "foo" - } - }, - "Settings": { - "FastEndpoints": { - "GlobalRoutePrefix": "elsa/api" - } - }, - "Features": ["Elsa", "WorkflowsApi", "Identity", "DefaultAuthentication"] - }] + "Shells": [ + { + "Name": "Default", + "Properties": { + "WebRouting": { + "Path": "foo" + } + }, + "Settings": { + "FastEndpoints": { + "GlobalRoutePrefix": "elsa/api" + }, + "SqliteWorkflowDefinitionPersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + }, + "SqliteWorkflowInstancePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + } + }, + "Features": [ + "Elsa", + "WorkflowsApi", + "Identity", + "DefaultAuthentication", + "SqliteWorkflowDefinitionPersistence", + "SqliteWorkflowInstancePersistence" + ] + } + ] } } diff --git a/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs index 5e744747bb..771a4cf69f 100644 --- a/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs +++ b/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs @@ -16,8 +16,7 @@ public abstract class DatabaseProviderConfigurator /// /// Gets or sets the connection string to use. /// - public Func ConnectionString { get; set; } - = _ => throw new InvalidOperationException("Connection string is required."); + public string ConnectionString { get; set; } = null!; /// /// Gets or sets additional options to configure the database context. @@ -53,6 +52,6 @@ protected abstract DbContextOptionsBuilder ConfigureProvider( /// public Action GetDbContextOptionsBuilder() { - return (sp, db) => ConfigureProvider(db, MigrationsAssembly, ConnectionString(sp), DbContextOptions, ProviderOptions); + return (sp, db) => ConfigureProvider(db, MigrationsAssembly, ConnectionString, DbContextOptions, ProviderOptions); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs index da8adc98e3..b931b50c3b 100644 --- a/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs @@ -1,12 +1,15 @@ +using CShells.Features; using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore; /// /// A generic shell feature that applies a database provider configuration to a persistence feature. +/// This feature automatically infers dependencies from the base persistence feature. /// public class DatabaseProviderShellFeature - : PersistenceShellFeatureBase, TDbContext> + : PersistenceShellFeatureBase, TDbContext>, + IInfersDependenciesFrom where TBaseFeature : PersistenceShellFeatureBase where TDbContext : ElsaDbContextBase { @@ -24,7 +27,7 @@ protected DatabaseProviderShellFeature(DatabaseProviderConfigurator /// Gets or sets the connection string to use. /// - public Func ConnectionString + public string ConnectionString { get => _providerConfigurator.ConnectionString; set => _providerConfigurator.ConnectionString = value; diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs index e37eac5640..725201341d 100644 --- a/src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs +++ b/src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs @@ -16,7 +16,6 @@ public class MySqlProviderConfigurator : DatabaseProviderConfiguratorThe assembly containing MySQL migrations. public MySqlProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) { - ConnectionString = _ => throw new InvalidOperationException("Connection string is required for MySQL."); } /// diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs index 6e186c4633..66d120333a 100644 --- a/src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs @@ -16,7 +16,6 @@ public class OracleProviderConfigurator : DatabaseProviderConfiguratorThe assembly containing Oracle migrations. public OracleProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) { - ConnectionString = _ => throw new InvalidOperationException("Connection string is required for Oracle."); } /// diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs index c1675b2844..1e420efdad 100644 --- a/src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs @@ -16,7 +16,6 @@ public class PostgreSqlProviderConfigurator : DatabaseProviderConfiguratorThe assembly containing PostgreSQL migrations. public PostgreSqlProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) { - ConnectionString = _ => throw new InvalidOperationException("Connection string is required for PostgreSQL."); } /// diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs index ad9fde47fa..34a816a77d 100644 --- a/src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs @@ -16,7 +16,6 @@ public class SqlServerProviderConfigurator : DatabaseProviderConfiguratorThe assembly containing SQL Server migrations. public SqlServerProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) { - ConnectionString = _ => throw new InvalidOperationException("Connection string is required for SQL Server."); } /// diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs index 5e7ac58afa..0032f5d92c 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs @@ -16,7 +16,6 @@ public class SqliteProviderConfigurator : DatabaseProviderConfiguratorThe assembly containing SQLite migrations. public SqliteProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) { - ConnectionString = _ => "Data Source=elsa.sqlite.db;Cache=Shared;"; } /// From 1400053f8863acbfd6203fe391cb56654ee12956 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 8 Feb 2026 12:21:25 +0100 Subject: [PATCH 28/36] Add persistence shell features for MySql, Oracle, PostgreSql, and Sqlite Introduced new shell features to configure MySql, Oracle, PostgreSql, and Sqlite persistence for workflow definitions and runtime data. Updated `appsettings.json` to replace individual Sqlite features with a unified `SqliteWorkflowPersistence`. Made minor code cleanup in `FastEndpointsFeature`. --- src/apps/Elsa.ModularServer.Web/appsettings.json | 10 ++++++++-- .../MySqlWorkflowPersistenceShellFeature.cs | 14 ++++++++++++++ .../OracleWorkflowPersistenceShellFeature.cs | 14 ++++++++++++++ .../PostgreSqlWorkflowPersistenceShellFeature.cs | 14 ++++++++++++++ ...eWorkflowDefinitionPersistenceShellFeature.cs | 16 ++++++++++++++++ 5 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowDefinitionPersistenceShellFeature.cs diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json index acf3367788..2463a412bd 100644 --- a/src/apps/Elsa.ModularServer.Web/appsettings.json +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -32,6 +32,13 @@ "EnableSensitiveDataLogging": false, "EnableDetailedErrors": false } + }, + "SqliteWorkflowRuntimePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } } }, "Features": [ @@ -39,8 +46,7 @@ "WorkflowsApi", "Identity", "DefaultAuthentication", - "SqliteWorkflowDefinitionPersistence", - "SqliteWorkflowInstancePersistence" + "SqliteWorkflowPersistence" ] } ] diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs new file mode 100644 index 0000000000..c5097ec886 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs @@ -0,0 +1,14 @@ +using CShells.Features; +using JetBrains.Annotations; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures; + +/// +/// Configures the persistence feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Persistence", + Description = "Provides MySql persistence for workflow definitions and runtime data", + DependsOn = ["MySqlWorkflowDefinitionPersistence", "MySqlWorkflowInstancePersistence", "MySqlWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class MySqlWorkflowPersistenceShellFeature; diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs new file mode 100644 index 0000000000..6fe54d69d6 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs @@ -0,0 +1,14 @@ +using CShells.Features; +using JetBrains.Annotations; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures; + +/// +/// Configures the persistence feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Persistence", + Description = "Provides Oracle persistence for workflow definitions and runtime data", + DependsOn = ["OracleWorkflowDefinitionPersistence", "OracleWorkflowInstancePersistence", "OracleWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class OracleWorkflowPersistenceShellFeature; diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs new file mode 100644 index 0000000000..ae2e65050c --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs @@ -0,0 +1,14 @@ +using CShells.Features; +using JetBrains.Annotations; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures; + +/// +/// Configures the persistence feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Persistence", + Description = "Provides PostgreSql persistence for workflow definitions and runtime data", + DependsOn = ["PostgreSqlWorkflowDefinitionPersistence", "PostgreSqlWorkflowInstancePersistence", "PostgreSqlWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class PostgreSqlWorkflowPersistenceShellFeature; diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..82d0b3a178 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,16 @@ +using CShells.Features; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures; + +/// +/// Configures the persistence feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Persistence", + Description = "Provides Sqlite persistence for workflow definitions and runtime data", + DependsOn = ["SqliteWorkflowDefinitionPersistence", "SqliteWorkflowInstancePersistence", "SqliteWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class SqliteWorkflowPersistenceShellFeature; \ No newline at end of file From c8cea9081e861d90d3c2898aa308408a6c2470ef Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 8 Feb 2026 12:37:51 +0100 Subject: [PATCH 29/36] Configure shell features for persistence Added `IServiceCollection` configuration for MySql, Oracle, PostgreSql, and Sqlite shell features to set up persistence services. --- .../Elsa.ModularServer.Web.csproj | 1 + .../MySqlWorkflowPersistenceShellFeature.cs | 8 +++++++- .../OracleWorkflowPersistenceShellFeature.cs | 8 +++++++- .../PostgreSqlWorkflowPersistenceShellFeature.cs | 8 +++++++- ...ure.cs => SqliteWorkflowPersistenceShellFeature.cs} | 10 +++++++--- 5 files changed, 29 insertions(+), 6 deletions(-) rename src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/{SqliteWorkflowDefinitionPersistenceShellFeature.cs => SqliteWorkflowPersistenceShellFeature.cs} (72%) diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj index a4776000ab..802154ad19 100644 --- a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -6,6 +6,7 @@ + diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs index c5097ec886..67dff18b86 100644 --- a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs @@ -1,5 +1,6 @@ using CShells.Features; using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.MySql.ShellFeatures; @@ -11,4 +12,9 @@ namespace Elsa.Persistence.EFCore.MySql.ShellFeatures; Description = "Provides MySql persistence for workflow definitions and runtime data", DependsOn = ["MySqlWorkflowDefinitionPersistence", "MySqlWorkflowInstancePersistence", "MySqlWorkflowRuntimePersistence"])] [UsedImplicitly] -public class MySqlWorkflowPersistenceShellFeature; +public class MySqlWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs index 6fe54d69d6..4ff31945eb 100644 --- a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs @@ -1,5 +1,6 @@ using CShells.Features; using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures; @@ -11,4 +12,9 @@ namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures; Description = "Provides Oracle persistence for workflow definitions and runtime data", DependsOn = ["OracleWorkflowDefinitionPersistence", "OracleWorkflowInstancePersistence", "OracleWorkflowRuntimePersistence"])] [UsedImplicitly] -public class OracleWorkflowPersistenceShellFeature; +public class OracleWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs index ae2e65050c..590a5c4d67 100644 --- a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs @@ -1,5 +1,6 @@ using CShells.Features; using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures; @@ -11,4 +12,9 @@ namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures; Description = "Provides PostgreSql persistence for workflow definitions and runtime data", DependsOn = ["PostgreSqlWorkflowDefinitionPersistence", "PostgreSqlWorkflowInstancePersistence", "PostgreSqlWorkflowRuntimePersistence"])] [UsedImplicitly] -public class PostgreSqlWorkflowPersistenceShellFeature; +public class PostgreSqlWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowPersistenceShellFeature.cs similarity index 72% rename from src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowDefinitionPersistenceShellFeature.cs rename to src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowPersistenceShellFeature.cs index 82d0b3a178..8e8e55f8fc 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowDefinitionPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowPersistenceShellFeature.cs @@ -1,7 +1,6 @@ using CShells.Features; -using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures; @@ -13,4 +12,9 @@ namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures; Description = "Provides Sqlite persistence for workflow definitions and runtime data", DependsOn = ["SqliteWorkflowDefinitionPersistence", "SqliteWorkflowInstancePersistence", "SqliteWorkflowRuntimePersistence"])] [UsedImplicitly] -public class SqliteWorkflowPersistenceShellFeature; \ No newline at end of file +public class SqliteWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} \ No newline at end of file From 6d88720df64fe4c851a945be46203848732d49d7 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 8 Feb 2026 21:39:52 +0100 Subject: [PATCH 30/36] Remove DatabaseProviderConfigurators and refactor persistence shell features Deleted DatabaseProviderConfigurator classes and restructured persistence shell features by integrating direct configuration logic for MySql, Oracle, PostgreSql, Sqlite, and SqlServer. Simplified configuration by inheriting from abstract shell feature base classes and removed redundant code. --- .../DatabaseProviderConfigurator.cs | 57 ------------------ .../DatabaseProviderShellFeature.cs | 60 ------------------- .../PersistenceShellFeatureBase.cs | 57 +++++++++++++----- .../MySqlProviderConfigurator.cs | 31 ---------- ...MySqlAlterationsPersistenceShellFeature.cs | 17 +++--- .../MySqlIdentityPersistenceShellFeature.cs | 17 +++--- .../MySqlLabelPersistenceShellFeature.cs | 17 +++--- ...rkflowDefinitionPersistenceShellFeature.cs | 17 +++--- ...WorkflowInstancePersistenceShellFeature.cs | 17 +++--- ...lWorkflowRuntimePersistenceShellFeature.cs | 17 +++--- .../MySqlTenantPersistenceShellFeature.cs | 17 +++--- .../OracleProviderConfigurator.cs | 33 ---------- ...racleAlterationsPersistenceShellFeature.cs | 17 +++--- .../OracleIdentityPersistenceShellFeature.cs | 17 +++--- .../OracleLabelPersistenceShellFeature.cs | 17 +++--- ...rkflowDefinitionPersistenceShellFeature.cs | 17 +++--- ...WorkflowInstancePersistenceShellFeature.cs | 17 +++--- ...eWorkflowRuntimePersistenceShellFeature.cs | 17 +++--- .../OracleTenantPersistenceShellFeature.cs | 17 +++--- .../PostgreSqlProviderConfigurator.cs | 31 ---------- ...reSqlAlterationsPersistenceShellFeature.cs | 17 +++--- ...stgreSqlIdentityPersistenceShellFeature.cs | 17 +++--- .../PostgreSqlLabelPersistenceShellFeature.cs | 17 +++--- ...rkflowDefinitionPersistenceShellFeature.cs | 17 +++--- ...WorkflowInstancePersistenceShellFeature.cs | 17 +++--- ...lWorkflowRuntimePersistenceShellFeature.cs | 17 +++--- ...PostgreSqlTenantPersistenceShellFeature.cs | 17 +++--- ...erverAlterationsPersistenceShellFeature.cs | 17 +++--- ...qlServerIdentityPersistenceShellFeature.cs | 17 +++--- .../SqlServerLabelPersistenceShellFeature.cs | 17 +++--- ...rkflowDefinitionPersistenceShellFeature.cs | 17 +++--- ...WorkflowInstancePersistenceShellFeature.cs | 17 +++--- ...rWorkflowRuntimePersistenceShellFeature.cs | 17 +++--- .../SqlServerTenantPersistenceShellFeature.cs | 17 +++--- .../SqlServerProviderConfigurator.cs | 31 ---------- ...qliteAlterationsPersistenceShellFeature.cs | 17 +++--- .../SqliteIdentityPersistenceShellFeature.cs | 17 +++--- .../SqliteLabelPersistenceShellFeature.cs | 17 +++--- ...rkflowDefinitionPersistenceShellFeature.cs | 17 +++--- ...WorkflowInstancePersistenceShellFeature.cs | 17 +++--- ...eWorkflowRuntimePersistenceShellFeature.cs | 17 +++--- .../SqliteTenantPersistenceShellFeature.cs | 17 +++--- .../SqliteProviderConfigurator.cs | 31 ---------- .../Modules/Alterations/ShellFeature.cs | 14 ++--- .../Modules/Identity/ShellFeature.cs | 14 ++--- .../Modules/Labels/ShellFeature.cs | 13 ++-- ...rkflowDefinitionPersistenceShellFeature.cs | 12 ++-- ...WorkflowInstancePersistenceShellFeature.cs | 12 ++-- .../WorkflowRuntimePersistenceShellFeature.cs | 19 +++--- .../Modules/Tenants/ShellFeature.cs | 12 ++-- .../WorkflowDefinitionsFeature.cs | 4 +- .../ShellFeatures/WorkflowInstancesFeature.cs | 4 +- .../WorkflowManagementFeature.cs | 13 ++-- .../Stores/CachingWorkflowDefinitionStore.cs | 2 +- 54 files changed, 412 insertions(+), 633 deletions(-) delete mode 100644 src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs delete mode 100644 src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs delete mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs delete mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs delete mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs delete mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs delete mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs diff --git a/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs deleted file mode 100644 index 771a4cf69f..0000000000 --- a/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderConfigurator.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Reflection; -using Microsoft.EntityFrameworkCore; - -namespace Elsa.Persistence.EFCore; - -/// -/// Base class for database provider configurations that can be applied to any persistence feature. -/// -public abstract class DatabaseProviderConfigurator -{ - /// - /// Gets the assembly containing migrations for this provider. - /// - protected Assembly MigrationsAssembly { get; } - - /// - /// Gets or sets the connection string to use. - /// - public string ConnectionString { get; set; } = null!; - - /// - /// Gets or sets additional options to configure the database context. - /// - public ElsaDbContextOptions? DbContextOptions { get; set; } - - /// - /// Gets or sets a callback to configure provider-specific options. - /// - public Action? ProviderOptions { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The assembly containing migrations for this provider. - protected DatabaseProviderConfigurator(Assembly migrationsAssembly) - { - MigrationsAssembly = migrationsAssembly; - } - - /// - /// Configures the database provider for the specified . - /// - protected abstract DbContextOptionsBuilder ConfigureProvider( - DbContextOptionsBuilder builder, - Assembly assembly, - string connectionString, - ElsaDbContextOptions? options, - Action? configure); - - /// - /// Gets the configuration delegate. - /// - public Action GetDbContextOptionsBuilder() - { - return (sp, db) => ConfigureProvider(db, MigrationsAssembly, ConnectionString, DbContextOptions, ProviderOptions); - } -} diff --git a/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs deleted file mode 100644 index b931b50c3b..0000000000 --- a/src/modules/Elsa.Persistence.EFCore.Common/DatabaseProviderShellFeature.cs +++ /dev/null @@ -1,60 +0,0 @@ -using CShells.Features; -using Microsoft.Extensions.DependencyInjection; - -namespace Elsa.Persistence.EFCore; - -/// -/// A generic shell feature that applies a database provider configuration to a persistence feature. -/// This feature automatically infers dependencies from the base persistence feature. -/// -public class DatabaseProviderShellFeature - : PersistenceShellFeatureBase, TDbContext>, - IInfersDependenciesFrom - where TBaseFeature : PersistenceShellFeatureBase - where TDbContext : ElsaDbContextBase -{ - private readonly DatabaseProviderConfigurator _providerConfigurator; - - /// - /// Initializes a new instance of the class. - /// - /// The database provider configurator to use. - protected DatabaseProviderShellFeature(DatabaseProviderConfigurator providerConfigurator) - { - _providerConfigurator = providerConfigurator; - } - - /// - /// Gets or sets the connection string to use. - /// - public string ConnectionString - { - get => _providerConfigurator.ConnectionString; - set => _providerConfigurator.ConnectionString = value; - } - - /// - /// Gets or sets additional options to configure the database context. - /// - public ElsaDbContextOptions? DbContextOptions - { - get => _providerConfigurator.DbContextOptions; - set => _providerConfigurator.DbContextOptions = value; - } - - /// - /// Gets or sets a callback to configure provider-specific options. - /// - public Action? ProviderOptions - { - get => _providerConfigurator.ProviderOptions; - set => _providerConfigurator.ProviderOptions = value; - } - - /// - protected override void OnConfiguring(IServiceCollection services) - { - DbContextOptionsBuilder = _providerConfigurator.GetDbContextOptionsBuilder(); - base.OnConfiguring(services); - } -} diff --git a/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs b/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs index f0c1ed50f5..4da028d57c 100644 --- a/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs +++ b/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs @@ -1,3 +1,4 @@ +using System.Reflection; using CShells.Features; using Elsa.Common.Entities; using Elsa.Extensions; @@ -8,37 +9,50 @@ // ReSharper disable once CheckNamespace namespace Elsa.Persistence.EFCore; -public abstract class PersistenceShellFeatureBase : IShellFeature +public abstract class PersistenceShellFeatureBase : IShellFeature where TDbContext : ElsaDbContextBase { /// /// Gets or sets a value indicating whether to use context pooling. /// - public virtual bool UseContextPooling { get; set; } + public bool UseContextPooling { get; set; } /// /// Gets or sets a value indicating whether to run migrations. /// - public virtual bool RunMigrations { get; set; } = true; + public bool RunMigrations { get; set; } = true; /// /// Gets or sets the lifetime of the . Defaults to . /// public ServiceLifetime DbContextFactoryLifetime { get; set; } = ServiceLifetime.Scoped; + /// + /// Gets or sets the connection string to use for the database. + /// + public string ConnectionString { get; set; } = null!; + + /// + /// Gets or sets additional options to configure the database context. + /// + public ElsaDbContextOptions? DbContextOptions { get; set; } + /// /// Gets or sets the callback used to configure the . /// - public virtual Action DbContextOptionsBuilder { get; set; } = null!; + protected virtual Action DbContextOptionsBuilder { get; set; } = (_, _) => { }; public void ConfigureServices(IServiceCollection services) { - if (DbContextOptionsBuilder == null) - throw new InvalidOperationException("The DbContextOptionsBuilder must be configured."); - Action setup = (sp, opts) => { opts.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); + + // Configure the database provider + var migrationsAssembly = GetMigrationsAssembly(); + ConfigureProvider(opts, migrationsAssembly, ConnectionString, DbContextOptions); + + // Allow derived classes to further configure DbContextOptionsBuilder(sp, opts); }; @@ -53,17 +67,32 @@ public void ConfigureServices(IServiceCollection services) { options.RunMigrations[typeof(TDbContext)] = RunMigrations; }); - - OnConfiguring(services); - } - protected virtual void OnConfiguring(IServiceCollection services) - { + services.AddStartupTask>(); + OnConfiguring(services); } - protected virtual void ConfigureMigrations(IServiceCollection services) + /// + /// Gets the assembly containing migrations for this provider. + /// By default, returns the assembly of the concrete feature type. + /// + protected virtual Assembly GetMigrationsAssembly() => GetType().Assembly; + + /// + /// Configures the database provider for the specified . + /// + /// The options builder to configure. + /// The assembly containing migrations. + /// The connection string to use. + /// Additional options to configure the database context. + protected abstract void ConfigureProvider( + DbContextOptionsBuilder builder, + Assembly migrationsAssembly, + string connectionString, + ElsaDbContextOptions? options); + + protected virtual void OnConfiguring(IServiceCollection services) { - services.AddStartupTask>(); } /// diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs deleted file mode 100644 index 725201341d..0000000000 --- a/src/modules/Elsa.Persistence.EFCore.MySql/MySqlProviderConfigurator.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Reflection; -using Elsa.Persistence.EFCore.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; - -namespace Elsa.Persistence.EFCore.MySql; - -/// -/// Configures MySQL as the database provider. -/// -public class MySqlProviderConfigurator : DatabaseProviderConfigurator -{ - /// - /// Initializes a new instance of the class. - /// - /// The assembly containing MySQL migrations. - public MySqlProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) - { - } - - /// - protected override DbContextOptionsBuilder ConfigureProvider( - DbContextOptionsBuilder builder, - Assembly assembly, - string connectionString, - ElsaDbContextOptions? options, - Action? configure) - { - return builder.UseElsaMySql(assembly, connectionString, options, configure: configure); - } -} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs index 1314aae4b4..62a740dfb2 100644 --- a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Alterations; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Alterations; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Alterations; /// [ShellFeature( DisplayName = "MySql Alterations Persistence", - Description = "Provides MySql persistence for workflow alterations")] + Description = "Provides MySql persistence for workflow alterations", + DependsOn = ["Alterations"])] [UsedImplicitly] public class MySqlAlterationsPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public MySqlAlterationsPersistenceShellFeature() - : base(new MySqlProviderConfigurator(typeof(MySqlAlterationsPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs index fa00ccb963..b9d0184ecc 100644 --- a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Identity; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Identity; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Identity; /// [ShellFeature( DisplayName = "MySql Identity Persistence", - Description = "Provides MySql persistence for identity management")] + Description = "Provides MySql persistence for identity management", + DependsOn = ["Identity"])] [UsedImplicitly] public class MySqlIdentityPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreIdentityPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public MySqlIdentityPersistenceShellFeature() - : base(new MySqlProviderConfigurator(typeof(MySqlIdentityPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs index 4dff1edae1..167bff57e6 100644 --- a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Labels; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Labels; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Labels; /// [ShellFeature( DisplayName = "MySql Label Persistence", - Description = "Provides MySql persistence for label management")] + Description = "Provides MySql persistence for label management", + DependsOn = ["Labels"])] [UsedImplicitly] public class MySqlLabelPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreLabelPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public MySqlLabelPersistenceShellFeature() - : base(new MySqlProviderConfigurator(typeof(MySqlLabelPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs index 961189201f..25a075c237 100644 --- a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Management; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Management; /// [ShellFeature( DisplayName = "MySql Workflow Definition Persistence", - Description = "Provides MySql persistence for workflow definitions")] + Description = "Provides MySql persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] [UsedImplicitly] public class MySqlWorkflowDefinitionPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public MySqlWorkflowDefinitionPersistenceShellFeature() - : base(new MySqlProviderConfigurator(typeof(MySqlWorkflowDefinitionPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs index b195a4526b..986a02f09b 100644 --- a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Management; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Management; /// [ShellFeature( DisplayName = "MySql Workflow Instance Persistence", - Description = "Provides MySql persistence for workflow instances")] + Description = "Provides MySql persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] [UsedImplicitly] public class MySqlWorkflowInstancePersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public MySqlWorkflowInstancePersistenceShellFeature() - : base(new MySqlProviderConfigurator(typeof(MySqlWorkflowInstancePersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs index b5bde8e69d..179a2b964d 100644 --- a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Runtime; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Runtime; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Runtime; /// [ShellFeature( DisplayName = "MySql Workflow Runtime Persistence", - Description = "Provides MySql persistence for workflow runtime")] + Description = "Provides MySql persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] [UsedImplicitly] public class MySqlWorkflowRuntimePersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public MySqlWorkflowRuntimePersistenceShellFeature() - : base(new MySqlProviderConfigurator(typeof(MySqlWorkflowRuntimePersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs index 1900e46a16..c5b7d14172 100644 --- a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Tenants; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Tenants; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Tenants; /// [ShellFeature( DisplayName = "MySql Tenant Persistence", - Description = "Provides MySql persistence for tenant management")] + Description = "Provides MySql persistence for tenant management", + DependsOn = ["TenantManagement"])] [UsedImplicitly] public class MySqlTenantPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreTenantManagementShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public MySqlTenantPersistenceShellFeature() - : base(new MySqlProviderConfigurator(typeof(MySqlTenantPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs deleted file mode 100644 index 66d120333a..0000000000 --- a/src/modules/Elsa.Persistence.EFCore.Oracle/OracleProviderConfigurator.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using Elsa.Persistence.EFCore.Extensions; -using Microsoft.EntityFrameworkCore; -using Oracle.EntityFrameworkCore.Infrastructure; - -namespace Elsa.Persistence.EFCore.Oracle; - -/// -/// Configures Oracle as the database provider. -/// -public class OracleProviderConfigurator : DatabaseProviderConfigurator -{ - /// - /// Initializes a new instance of the class. - /// - /// The assembly containing Oracle migrations. - public OracleProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) - { - } - - /// - protected override DbContextOptionsBuilder ConfigureProvider( - DbContextOptionsBuilder builder, - Assembly assembly, - string connectionString, - ElsaDbContextOptions? options, - Action? configure) - { - DbContextOptions ??= new(); - DbContextOptions.Configure(); - return builder.UseElsaOracle(assembly, connectionString, DbContextOptions, configure); - } -} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs index 22c3b69b12..e5417957c1 100644 --- a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Alterations; using JetBrains.Annotations; -using Oracle.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Alterations; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Alterations; /// [ShellFeature( DisplayName = "Oracle Alterations Persistence", - Description = "Provides Oracle persistence for workflow alterations")] + Description = "Provides Oracle persistence for workflow alterations", + DependsOn = ["Alterations"])] [UsedImplicitly] public class OracleAlterationsPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public OracleAlterationsPersistenceShellFeature() - : base(new OracleProviderConfigurator(typeof(OracleAlterationsPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs index 702a33e3db..7b77d93071 100644 --- a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Identity; using JetBrains.Annotations; -using Oracle.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Identity; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Identity; /// [ShellFeature( DisplayName = "Oracle Identity Persistence", - Description = "Provides Oracle persistence for identity management")] + Description = "Provides Oracle persistence for identity management", + DependsOn = ["Identity"])] [UsedImplicitly] public class OracleIdentityPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreIdentityPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public OracleIdentityPersistenceShellFeature() - : base(new OracleProviderConfigurator(typeof(OracleIdentityPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs index b6fc65b07b..566efe2ef6 100644 --- a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Labels; using JetBrains.Annotations; -using Oracle.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Labels; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Labels; /// [ShellFeature( DisplayName = "Oracle Label Persistence", - Description = "Provides Oracle persistence for label management")] + Description = "Provides Oracle persistence for label management", + DependsOn = ["Labels"])] [UsedImplicitly] public class OracleLabelPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreLabelPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public OracleLabelPersistenceShellFeature() - : base(new OracleProviderConfigurator(typeof(OracleLabelPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs index 6040dda87d..a218bf9874 100644 --- a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Oracle.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Management; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Management; /// [ShellFeature( DisplayName = "Oracle Workflow Definition Persistence", - Description = "Provides Oracle persistence for workflow definitions")] + Description = "Provides Oracle persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] [UsedImplicitly] public class OracleWorkflowDefinitionPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public OracleWorkflowDefinitionPersistenceShellFeature() - : base(new OracleProviderConfigurator(typeof(OracleWorkflowDefinitionPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs index 92c1426388..6c7ab5f961 100644 --- a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Oracle.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Management; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Management; /// [ShellFeature( DisplayName = "Oracle Workflow Instance Persistence", - Description = "Provides Oracle persistence for workflow instances")] + Description = "Provides Oracle persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] [UsedImplicitly] public class OracleWorkflowInstancePersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public OracleWorkflowInstancePersistenceShellFeature() - : base(new OracleProviderConfigurator(typeof(OracleWorkflowInstancePersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs index 72ba04548f..4547d14862 100644 --- a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Runtime; using JetBrains.Annotations; -using Oracle.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Runtime; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Runtime; /// [ShellFeature( DisplayName = "Oracle Workflow Runtime Persistence", - Description = "Provides Oracle persistence for workflow runtime")] + Description = "Provides Oracle persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] [UsedImplicitly] public class OracleWorkflowRuntimePersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public OracleWorkflowRuntimePersistenceShellFeature() - : base(new OracleProviderConfigurator(typeof(OracleWorkflowRuntimePersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs index a139c5e09d..59ced9697c 100644 --- a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Tenants; using JetBrains.Annotations; -using Oracle.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Tenants; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Tenants; /// [ShellFeature( DisplayName = "Oracle Tenant Persistence", - Description = "Provides Oracle persistence for tenant management")] + Description = "Provides Oracle persistence for tenant management", + DependsOn = ["TenantManagement"])] [UsedImplicitly] public class OracleTenantPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreTenantManagementShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public OracleTenantPersistenceShellFeature() - : base(new OracleProviderConfigurator(typeof(OracleTenantPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs deleted file mode 100644 index 1e420efdad..0000000000 --- a/src/modules/Elsa.Persistence.EFCore.PostgreSql/PostgreSqlProviderConfigurator.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Reflection; -using Elsa.Persistence.EFCore.Extensions; -using Microsoft.EntityFrameworkCore; -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; - -namespace Elsa.Persistence.EFCore.PostgreSql; - -/// -/// Configures PostgreSQL as the database provider. -/// -public class PostgreSqlProviderConfigurator : DatabaseProviderConfigurator -{ - /// - /// Initializes a new instance of the class. - /// - /// The assembly containing PostgreSQL migrations. - public PostgreSqlProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) - { - } - - /// - protected override DbContextOptionsBuilder ConfigureProvider( - DbContextOptionsBuilder builder, - Assembly assembly, - string connectionString, - ElsaDbContextOptions? options, - Action? configure) - { - return builder.UseElsaPostgreSql(assembly, connectionString, options, configure); - } -} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs index 5630b36bca..ad341f083d 100644 --- a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Alterations; using JetBrains.Annotations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Alterations; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Alterations; /// [ShellFeature( DisplayName = "PostgreSql Alterations Persistence", - Description = "Provides PostgreSql persistence for workflow alterations")] + Description = "Provides PostgreSql persistence for workflow alterations", + DependsOn = ["Alterations"])] [UsedImplicitly] public class PostgreSqlAlterationsPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public PostgreSqlAlterationsPersistenceShellFeature() - : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlAlterationsPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs index 5836699895..5544db1910 100644 --- a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Identity; using JetBrains.Annotations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Identity; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Identity; /// [ShellFeature( DisplayName = "PostgreSql Identity Persistence", - Description = "Provides PostgreSql persistence for identity management")] + Description = "Provides PostgreSql persistence for identity management", + DependsOn = ["Identity"])] [UsedImplicitly] public class PostgreSqlIdentityPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreIdentityPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public PostgreSqlIdentityPersistenceShellFeature() - : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlIdentityPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs index 7418ec6d6f..53df538475 100644 --- a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Labels; using JetBrains.Annotations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Labels; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Labels; /// [ShellFeature( DisplayName = "PostgreSql Label Persistence", - Description = "Provides PostgreSql persistence for label management")] + Description = "Provides PostgreSql persistence for label management", + DependsOn = ["Labels"])] [UsedImplicitly] public class PostgreSqlLabelPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreLabelPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public PostgreSqlLabelPersistenceShellFeature() - : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlLabelPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs index a75d1deec1..c79f66d215 100644 --- a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Management; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Management; /// [ShellFeature( DisplayName = "PostgreSql Workflow Definition Persistence", - Description = "Provides PostgreSql persistence for workflow definitions")] + Description = "Provides PostgreSql persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] [UsedImplicitly] public class PostgreSqlWorkflowDefinitionPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public PostgreSqlWorkflowDefinitionPersistenceShellFeature() - : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlWorkflowDefinitionPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs index 46e66699e9..4ec10667ad 100644 --- a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Management; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Management; /// [ShellFeature( DisplayName = "PostgreSql Workflow Instance Persistence", - Description = "Provides PostgreSql persistence for workflow instances")] + Description = "Provides PostgreSql persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] [UsedImplicitly] public class PostgreSqlWorkflowInstancePersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public PostgreSqlWorkflowInstancePersistenceShellFeature() - : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlWorkflowInstancePersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs index d930862e4e..21c5079c99 100644 --- a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Runtime; using JetBrains.Annotations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Runtime; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Runtime; /// [ShellFeature( DisplayName = "PostgreSql Workflow Runtime Persistence", - Description = "Provides PostgreSql persistence for workflow runtime")] + Description = "Provides PostgreSql persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] [UsedImplicitly] public class PostgreSqlWorkflowRuntimePersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public PostgreSqlWorkflowRuntimePersistenceShellFeature() - : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlWorkflowRuntimePersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs index f34c3c286b..9f9ff77f3f 100644 --- a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Tenants; using JetBrains.Annotations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Tenants; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Tenants; /// [ShellFeature( DisplayName = "PostgreSql Tenant Persistence", - Description = "Provides PostgreSql persistence for tenant management")] + Description = "Provides PostgreSql persistence for tenant management", + DependsOn = ["TenantManagement"])] [UsedImplicitly] public class PostgreSqlTenantPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreTenantManagementShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public PostgreSqlTenantPersistenceShellFeature() - : base(new PostgreSqlProviderConfigurator(typeof(PostgreSqlTenantPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs index 43a96ea637..f47c2b1cbe 100644 --- a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Alterations; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Alterations; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Alterations; /// [ShellFeature( DisplayName = "SqlServer Alterations Persistence", - Description = "Provides SqlServer persistence for workflow alterations")] + Description = "Provides SqlServer persistence for workflow alterations", + DependsOn = ["Alterations"])] [UsedImplicitly] public class SqlServerAlterationsPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqlServerAlterationsPersistenceShellFeature() - : base(new SqlServerProviderConfigurator(typeof(SqlServerAlterationsPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs index 92cb7b3bdc..bc1ee0e687 100644 --- a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Identity; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Identity; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Identity; /// [ShellFeature( DisplayName = "SqlServer Identity Persistence", - Description = "Provides SqlServer persistence for identity management")] + Description = "Provides SqlServer persistence for identity management", + DependsOn = ["Identity"])] [UsedImplicitly] public class SqlServerIdentityPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreIdentityPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqlServerIdentityPersistenceShellFeature() - : base(new SqlServerProviderConfigurator(typeof(SqlServerIdentityPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs index f5cb57b0c2..ca3a528b91 100644 --- a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Labels; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Labels; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Labels; /// [ShellFeature( DisplayName = "SqlServer Label Persistence", - Description = "Provides SqlServer persistence for label management")] + Description = "Provides SqlServer persistence for label management", + DependsOn = ["Labels"])] [UsedImplicitly] public class SqlServerLabelPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreLabelPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqlServerLabelPersistenceShellFeature() - : base(new SqlServerProviderConfigurator(typeof(SqlServerLabelPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs index 61e28bc0c5..7635fe3ba6 100644 --- a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Management; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Management; /// [ShellFeature( DisplayName = "SqlServer Workflow Definition Persistence", - Description = "Provides SqlServer persistence for workflow definitions")] + Description = "Provides SqlServer persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] [UsedImplicitly] public class SqlServerWorkflowDefinitionPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqlServerWorkflowDefinitionPersistenceShellFeature() - : base(new SqlServerProviderConfigurator(typeof(SqlServerWorkflowDefinitionPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs index a2bdc498f7..176c1bb04a 100644 --- a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Management; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Management; /// [ShellFeature( DisplayName = "SqlServer Workflow Instance Persistence", - Description = "Provides SqlServer persistence for workflow instances")] + Description = "Provides SqlServer persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] [UsedImplicitly] public class SqlServerWorkflowInstancePersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqlServerWorkflowInstancePersistenceShellFeature() - : base(new SqlServerProviderConfigurator(typeof(SqlServerWorkflowInstancePersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs index b49fc708cd..65136a99cc 100644 --- a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Runtime; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Runtime; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Runtime; /// [ShellFeature( DisplayName = "SqlServer Workflow Runtime Persistence", - Description = "Provides SqlServer persistence for workflow runtime")] + Description = "Provides SqlServer persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] [UsedImplicitly] public class SqlServerWorkflowRuntimePersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqlServerWorkflowRuntimePersistenceShellFeature() - : base(new SqlServerProviderConfigurator(typeof(SqlServerWorkflowRuntimePersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs index 7740bab216..16e6663266 100644 --- a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Tenants; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Tenants; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Tenants; /// [ShellFeature( DisplayName = "SqlServer Tenant Persistence", - Description = "Provides SqlServer persistence for tenant management")] + Description = "Provides SqlServer persistence for tenant management", + DependsOn = ["TenantManagement"])] [UsedImplicitly] public class SqlServerTenantPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreTenantManagementShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqlServerTenantPersistenceShellFeature() - : base(new SqlServerProviderConfigurator(typeof(SqlServerTenantPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs deleted file mode 100644 index 34a816a77d..0000000000 --- a/src/modules/Elsa.Persistence.EFCore.SqlServer/SqlServerProviderConfigurator.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Reflection; -using Elsa.Persistence.EFCore.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; - -namespace Elsa.Persistence.EFCore.SqlServer; - -/// -/// Configures SQL Server as the database provider. -/// -public class SqlServerProviderConfigurator : DatabaseProviderConfigurator -{ - /// - /// Initializes a new instance of the class. - /// - /// The assembly containing SQL Server migrations. - public SqlServerProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) - { - } - - /// - protected override DbContextOptionsBuilder ConfigureProvider( - DbContextOptionsBuilder builder, - Assembly assembly, - string connectionString, - ElsaDbContextOptions? options, - Action? configure) - { - return builder.UseElsaSqlServer(assembly, connectionString, options, configure); - } -} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs index 2bbc4258e2..e3e6b6726d 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Alterations; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Alterations; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Alterations; /// [ShellFeature( DisplayName = "Sqlite Alterations Persistence", - Description = "Provides Sqlite persistence for workflow alterations")] + Description = "Provides Sqlite persistence for workflow alterations", + DependsOn = ["Alterations"])] [UsedImplicitly] public class SqliteAlterationsPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqliteAlterationsPersistenceShellFeature() - : base(new SqliteProviderConfigurator(typeof(SqliteAlterationsPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs index 7b12feef7b..baffb2080f 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Identity; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Identity; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Identity; /// [ShellFeature( DisplayName = "Sqlite Identity Persistence", - Description = "Provides Sqlite persistence for identity management")] + Description = "Provides Sqlite persistence for identity management", + DependsOn = ["Identity"])] [UsedImplicitly] public class SqliteIdentityPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreIdentityPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqliteIdentityPersistenceShellFeature() - : base(new SqliteProviderConfigurator(typeof(SqliteIdentityPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs index 9238593b57..4bf95c2b0c 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Labels; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Labels; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Labels; /// [ShellFeature( DisplayName = "Sqlite Label Persistence", - Description = "Provides Sqlite persistence for label management")] + Description = "Provides Sqlite persistence for label management", + DependsOn = ["Labels"])] [UsedImplicitly] public class SqliteLabelPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreLabelPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqliteLabelPersistenceShellFeature() - : base(new SqliteProviderConfigurator(typeof(SqliteLabelPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs index df9dd3dced..dfee4107ab 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; /// [ShellFeature( DisplayName = "Sqlite Workflow Definition Persistence", - Description = "Provides Sqlite persistence for workflow definitions")] + Description = "Provides Sqlite persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] [UsedImplicitly] public class SqliteWorkflowDefinitionPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqliteWorkflowDefinitionPersistenceShellFeature() - : base(new SqliteProviderConfigurator(typeof(SqliteWorkflowDefinitionPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs index 77c9361927..2b935dff31 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; /// [ShellFeature( DisplayName = "Sqlite Workflow Instance Persistence", - Description = "Provides Sqlite persistence for workflow instances")] + Description = "Provides Sqlite persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] [UsedImplicitly] public class SqliteWorkflowInstancePersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqliteWorkflowInstancePersistenceShellFeature() - : base(new SqliteProviderConfigurator(typeof(SqliteWorkflowInstancePersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs index 52451d0b73..d05a468685 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Runtime; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Runtime; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Runtime; /// [ShellFeature( DisplayName = "Sqlite Workflow Runtime Persistence", - Description = "Provides Sqlite persistence for workflow runtime")] + Description = "Provides Sqlite persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] [UsedImplicitly] public class SqliteWorkflowRuntimePersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqliteWorkflowRuntimePersistenceShellFeature() - : base(new SqliteProviderConfigurator(typeof(SqliteWorkflowRuntimePersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs index 413b207ae1..a53eeedeb8 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs @@ -1,7 +1,9 @@ +using System.Reflection; using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; using Elsa.Persistence.EFCore.Modules.Tenants; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Tenants; @@ -10,16 +12,15 @@ namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Tenants; /// [ShellFeature( DisplayName = "Sqlite Tenant Persistence", - Description = "Provides Sqlite persistence for tenant management")] + Description = "Provides Sqlite persistence for tenant management", + DependsOn = ["TenantManagement"])] [UsedImplicitly] public class SqliteTenantPersistenceShellFeature - : DatabaseProviderShellFeature + : EFCoreTenantManagementShellFeatureBase { - /// - /// Initializes a new instance of the class. - /// - public SqliteTenantPersistenceShellFeature() - : base(new SqliteProviderConfigurator(typeof(SqliteTenantPersistenceShellFeature).Assembly)) + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs deleted file mode 100644 index 0032f5d92c..0000000000 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/SqliteProviderConfigurator.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Reflection; -using Elsa.Persistence.EFCore.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; - -namespace Elsa.Persistence.EFCore.Sqlite; - -/// -/// Configures SQLite as the database provider. -/// -public class SqliteProviderConfigurator : DatabaseProviderConfigurator -{ - /// - /// Initializes a new instance of the class. - /// - /// The assembly containing SQLite migrations. - public SqliteProviderConfigurator(Assembly migrationsAssembly) : base(migrationsAssembly) - { - } - - /// - protected override DbContextOptionsBuilder ConfigureProvider( - DbContextOptionsBuilder builder, - Assembly assembly, - string connectionString, - ElsaDbContextOptions? options, - Action? configure) - { - return builder.UseElsaSqlite(assembly, connectionString, options, configure); - } -} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs index cca6269129..0a3aea1ab6 100644 --- a/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs @@ -1,4 +1,4 @@ -using CShells.Features; +using Elsa.Alterations.Core.Contracts; using Elsa.Alterations.Core.Entities; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; @@ -6,17 +6,17 @@ namespace Elsa.Persistence.EFCore.Modules.Alterations; /// -/// Configures the alterations feature with an Entity Framework Core persistence provider. +/// Base class for alterations persistence features. +/// This is not a standalone shell feature - use provider-specific features. /// -[ShellFeature( - DisplayName = "EF Core Alterations Persistence", - Description = "Provides Entity Framework Core persistence for workflow alterations", - DependsOn = ["Alterations"])] [UsedImplicitly] -public class EFCoreAlterationsPersistenceShellFeature : PersistenceShellFeatureBase +public abstract class EFCoreAlterationsPersistenceShellFeatureBase : PersistenceShellFeatureBase { protected override void OnConfiguring(IServiceCollection services) { + services.AddScoped(); + services.AddScoped(); + AddEntityStore(services); AddEntityStore(services); } diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs index 1a0060c44a..a09f2658b0 100644 --- a/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs @@ -1,4 +1,4 @@ -using CShells.Features; +using Elsa.Identity.Contracts; using Elsa.Identity.Entities; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; @@ -6,17 +6,17 @@ namespace Elsa.Persistence.EFCore.Modules.Identity; /// -/// Configures the identity feature with Entity Framework Core persistence providers. +/// Base class for identity persistence features. +/// This is not a standalone shell feature - use provider-specific features. /// -[ShellFeature( - DisplayName = "EF Core Identity Persistence", - Description = "Provides Entity Framework Core persistence for identity management", - DependsOn = ["Identity"])] [UsedImplicitly] -public class EFCoreIdentityPersistenceShellFeature : PersistenceShellFeatureBase +public abstract class EFCoreIdentityPersistenceShellFeatureBase : PersistenceShellFeatureBase { protected override void OnConfiguring(IServiceCollection services) { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); AddEntityStore(services); AddEntityStore(services); AddEntityStore(services); diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs index 9b1a6888b9..20c9fa9370 100644 --- a/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs @@ -1,4 +1,4 @@ -using CShells.Features; +using Elsa.Labels.Contracts; using Elsa.Labels.Entities; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; @@ -6,17 +6,16 @@ namespace Elsa.Persistence.EFCore.Modules.Labels; /// -/// Configures the labels feature with an Entity Framework Core persistence provider. +/// Base class for label persistence features. +/// This is not a standalone shell feature - use provider-specific features. /// -[ShellFeature( - DisplayName = "EF Core Label Persistence", - Description = "Provides Entity Framework Core persistence for label management", - DependsOn = ["Labels"])] [UsedImplicitly] -public class EFCoreLabelPersistenceShellFeature : PersistenceShellFeatureBase +public abstract class EFCoreLabelPersistenceShellFeatureBase : PersistenceShellFeatureBase { protected override void OnConfiguring(IServiceCollection services) { + services.AddScoped(); + services.AddScoped(); AddEntityStore(services); AddEntityStore(services); } diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs index d6171caf4d..7155c603ec 100644 --- a/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs @@ -1,4 +1,4 @@ -using CShells.Features; +using Elsa.Workflows.Management; using Elsa.Workflows.Management.Entities; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; @@ -6,17 +6,15 @@ namespace Elsa.Persistence.EFCore.Modules.Management; /// -/// Configures the workflow definitions feature with an Entity Framework Core persistence provider. +/// Base class for workflow definition persistence features. +/// This is not a standalone shell feature - use provider-specific features like SqliteWorkflowDefinitionPersistenceShellFeature. /// -[ShellFeature( - DisplayName = "EF Core Workflow Definition Persistence", - Description = "Provides Entity Framework Core persistence for workflow definitions", - DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] [UsedImplicitly] -public class EFCoreWorkflowDefinitionPersistenceShellFeature : PersistenceShellFeatureBase +public abstract class EFCoreWorkflowDefinitionPersistenceShellFeatureBase : PersistenceShellFeatureBase { protected override void OnConfiguring(IServiceCollection services) { + services.AddScoped(); AddEntityStore(services); } } diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs index a03ca36e00..200c581fc4 100644 --- a/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs @@ -1,4 +1,4 @@ -using CShells.Features; +using Elsa.Workflows.Management; using Elsa.Workflows.Management.Entities; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; @@ -6,17 +6,15 @@ namespace Elsa.Persistence.EFCore.Modules.Management; /// -/// Configures the workflow instances feature with an Entity Framework Core persistence provider. +/// Base class for workflow instance persistence features. +/// This is not a standalone shell feature - use provider-specific features. /// -[ShellFeature( - DisplayName = "EF Core Workflow Instance Persistence", - Description = "Provides Entity Framework Core persistence for workflow instances", - DependsOn = ["WorkflowManagement", "WorkflowInstances"])] [UsedImplicitly] -public class EFCoreWorkflowInstancePersistenceShellFeature : PersistenceShellFeatureBase +public abstract class EFCoreWorkflowInstancePersistenceShellFeatureBase : PersistenceShellFeatureBase { protected override void OnConfiguring(IServiceCollection services) { + services.AddScoped(); AddEntityStore(services); } } diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs index 4f761dd6d1..1017ae80df 100644 --- a/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs @@ -1,5 +1,6 @@ -using CShells.Features; +using Elsa.KeyValues.Contracts; using Elsa.KeyValues.Entities; +using Elsa.Workflows.Runtime; using Elsa.Workflows.Runtime.Entities; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; @@ -7,17 +8,21 @@ namespace Elsa.Persistence.EFCore.Modules.Runtime; /// -/// Configures the workflow runtime feature with Entity Framework Core persistence providers. +/// Base class for workflow runtime persistence features. +/// This is not a standalone shell feature - use provider-specific features. /// -[ShellFeature( - DisplayName = "EF Core Workflow Runtime Persistence", - Description = "Provides Entity Framework Core persistence for workflow runtime", - DependsOn = ["WorkflowRuntime"])] [UsedImplicitly] -public class EFCoreWorkflowRuntimePersistenceShellFeature : PersistenceShellFeatureBase +public abstract class EFCoreWorkflowRuntimePersistenceShellFeatureBase : PersistenceShellFeatureBase { protected override void OnConfiguring(IServiceCollection services) { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + AddEntityStore(services); AddStore(services); AddStore(services); diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs index d8abd6ce5a..f646993940 100644 --- a/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs @@ -1,22 +1,20 @@ -using CShells.Features; using Elsa.Common.Multitenancy; +using Elsa.Tenants; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.Modules.Tenants; /// -/// Configures the tenant management feature with an Entity Framework Core persistence provider. +/// Base class for tenant management persistence features. +/// This is not a standalone shell feature - use provider-specific features. /// -[ShellFeature( - DisplayName = "EF Core Tenant Management Persistence", - Description = "Provides Entity Framework Core persistence for tenant management", - DependsOn = ["TenantManagement"])] [UsedImplicitly] -public class EFCoreTenantManagementShellFeature : PersistenceShellFeatureBase +public abstract class EFCoreTenantManagementShellFeatureBase : PersistenceShellFeatureBase { protected override void OnConfiguring(IServiceCollection services) { + services.AddScoped(); AddEntityStore(services); } } diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs index aeec77e0bf..a79a6ee1f1 100644 --- a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs @@ -14,10 +14,8 @@ namespace Elsa.Workflows.Management.ShellFeatures; [UsedImplicitly] public class WorkflowDefinitionsFeature : IShellFeature { - private Func WorkflowDefinitionStore { get; set; } = sp => sp.GetRequiredService(); - public void ConfigureServices(IServiceCollection services) { - services.AddScoped(WorkflowDefinitionStore); + services.AddScoped(); } } diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs index accf031dc4..c9d072c369 100644 --- a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs @@ -14,10 +14,8 @@ namespace Elsa.Workflows.Management.ShellFeatures; [UsedImplicitly] public class WorkflowInstancesFeature : IShellFeature { - private Func WorkflowInstanceStore { get; set; } = sp => sp.GetRequiredService(); - public void ConfigureServices(IServiceCollection services) { - services.AddScoped(WorkflowInstanceStore); + services.AddScoped(); } } diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs index f5476f2c85..ecc3036335 100644 --- a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs @@ -51,11 +51,6 @@ public class WorkflowManagementFeature : IShellFeature private const string DataCategory = "Data"; private const string SystemCategory = "System"; - private Func WorkflowDefinitionStore { get; set; } = sp => sp.GetRequiredService(); - private Func WorkflowInstanceStore { get; set; } = sp => sp.GetRequiredService(); - private Func WorkflowDefinitionPublisher { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); - private Func WorkflowReferenceQuery { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); - /// /// A set of variable types to make available to the system. /// @@ -98,8 +93,8 @@ public void ConfigureServices(IServiceCollection services) .AddScoped() .AddScoped() .AddScoped() - .AddScoped(WorkflowReferenceQuery) - .AddScoped(WorkflowDefinitionPublisher) + .AddScoped() + .AddScoped() .AddScoped() .AddScoped() .AddScoped() @@ -116,8 +111,8 @@ public void ConfigureServices(IServiceCollection services) .AddScoped() .AddSingleton() .AddSingleton() - .AddScoped(WorkflowInstanceStore) - .AddScoped(WorkflowDefinitionStore) + .AddScoped() + .AddScoped() .AddSingleton() .Decorate() .Decorate() diff --git a/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs b/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs index 67a7be6e80..c7facf34d7 100644 --- a/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs +++ b/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs @@ -129,7 +129,7 @@ public async Task CountDistinctAsync(CancellationToken cancellationToken = } /// - public async Task GetIsNameUnique(string name, string? definitionId = default, CancellationToken cancellationToken = default) + public async Task GetIsNameUnique(string name, string? definitionId = null, CancellationToken cancellationToken = default) { var cacheKey = hasher.Hash(nameof(GetIsNameUnique), name, definitionId); return await GetOrCreateAsync(cacheKey, () => decoratedStore.GetIsNameUnique(name, definitionId, cancellationToken)); From d829486f324f196ba2f389a7377f99c33b826d9b Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 8 Feb 2026 21:43:09 +0100 Subject: [PATCH 31/36] Correct IWorkflowDefinitionPublisher registration to use WorkflowDefinitionPublisher implementation --- .../ShellFeatures/WorkflowManagementFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs index ecc3036335..65b632402f 100644 --- a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs @@ -94,7 +94,7 @@ public void ConfigureServices(IServiceCollection services) .AddScoped() .AddScoped() .AddScoped() - .AddScoped() + .AddScoped() .AddScoped() .AddScoped() .AddScoped() From 83b9cdd440fc8b8ec4b6a187e7333fa9674c605a Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 9 Feb 2026 10:41:15 +0100 Subject: [PATCH 32/36] Add resilience feature and scoped services configuration for Sqlite persistence - Integrated `Microsoft.Extensions.DependencyInjection` to shell features for Sqlite persistence. - Updated `appsettings.json` and project references to include a new 'Resilience' feature. - Changed `ICommitStateHandler` service registration in `WorkflowRuntimeFeature` to use an implementation. --- .../Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj | 1 + src/apps/Elsa.ModularServer.Web/appsettings.json | 1 + .../SqliteAlterationsPersistenceShellFeature.cs | 8 ++++++++ .../Identity/SqliteIdentityPersistenceShellFeature.cs | 8 ++++++++ .../Labels/SqliteLabelPersistenceShellFeature.cs | 8 ++++++++ .../SqliteWorkflowDefinitionPersistenceShellFeature.cs | 8 ++++++++ .../SqliteWorkflowInstancePersistenceShellFeature.cs | 8 ++++++++ .../SqliteWorkflowRuntimePersistenceShellFeature.cs | 8 ++++++++ .../Tenants/SqliteTenantPersistenceShellFeature.cs | 8 ++++++++ .../Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs | 4 +++- .../ShellFeatures/WorkflowRuntimeFeature.cs | 3 ++- 11 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj index 802154ad19..b91683106f 100644 --- a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -7,6 +7,7 @@ + diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json index 2463a412bd..e3ca4a7be2 100644 --- a/src/apps/Elsa.ModularServer.Web/appsettings.json +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -46,6 +46,7 @@ "WorkflowsApi", "Identity", "DefaultAuthentication", + "Resilience", "SqliteWorkflowPersistence" ] } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs index e3e6b6726d..253e967ec3 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs @@ -4,6 +4,7 @@ using Elsa.Persistence.EFCore.Modules.Alterations; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Alterations; @@ -23,4 +24,11 @@ protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assem { builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs index baffb2080f..13ac4baab9 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs @@ -4,6 +4,7 @@ using Elsa.Persistence.EFCore.Modules.Identity; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Identity; @@ -23,4 +24,11 @@ protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assem { builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs index 4bf95c2b0c..c1150748c2 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs @@ -4,6 +4,7 @@ using Elsa.Persistence.EFCore.Modules.Labels; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Labels; @@ -23,4 +24,11 @@ protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assem { builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs index dfee4107ab..7cca81a699 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs @@ -4,6 +4,7 @@ using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; @@ -23,4 +24,11 @@ protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assem { builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs index 2b935dff31..6de74788b9 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs @@ -4,6 +4,7 @@ using Elsa.Persistence.EFCore.Modules.Management; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; @@ -23,4 +24,11 @@ protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assem { builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs index d05a468685..1632f5d966 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs @@ -4,6 +4,7 @@ using Elsa.Persistence.EFCore.Modules.Runtime; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Runtime; @@ -23,4 +24,11 @@ protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assem { builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } } diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs index a53eeedeb8..eea341eec8 100644 --- a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs @@ -4,6 +4,7 @@ using Elsa.Persistence.EFCore.Modules.Tenants; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Tenants; @@ -23,4 +24,11 @@ protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assem { builder.UseElsaSqlite(migrationsAssembly, connectionString, options); } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } } diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs index 273b003116..4c906c1617 100644 --- a/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs @@ -87,7 +87,6 @@ public void ConfigureServices(IServiceCollection services) .AddScoped() .AddSingleton() .AddSingleton() - .AddScoped() .AddSingleton() .AddSingleton() .AddSingleton() @@ -179,5 +178,8 @@ public void ConfigureServices(IServiceCollection services) // Register FlowchartOptions services.AddOptions(); + + // Overridable services + services.AddScoped(); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs index f801f354f5..c3276cf81c 100644 --- a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs +++ b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs @@ -2,6 +2,7 @@ using Elsa.Common.RecurringTasks; using Elsa.Extensions; using Elsa.Mediator.Contracts; +using Elsa.Workflows.CommitStates; using Elsa.Workflows.Management; using Elsa.Workflows.Management.Contracts; using Elsa.Workflows.Management.Services; @@ -194,7 +195,7 @@ public void ConfigureServices(IServiceCollection services) .AddScoped, WorkflowExecutionLogRecordExtractor>() .AddScoped() .AddScoped() - .AddScoped() + .AddScoped() .AddScoped() // Deprecated services. From 77053de2da60bcb2220983432e9a68cbfcddbcaa Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 9 Feb 2026 10:41:24 +0100 Subject: [PATCH 33/36] Add `ResilienceShellFeature` for configuring resilience strategies - Implemented new `ResilienceShellFeature` class to manage services related to resilience features. - Added scoped and singleton service registrations for resilience strategies, exception detection, and activity invocation. - Configured expression options for resilience handling in workflows. --- .../ShellFeatures/ResilienceShellFeature.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs diff --git a/src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs b/src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs new file mode 100644 index 0000000000..7087c4d3bc --- /dev/null +++ b/src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs @@ -0,0 +1,47 @@ +using CShells.FastEndpoints.Features; +using CShells.Features; +using Elsa.Expressions.Options; +using Elsa.Extensions; +using Elsa.Resilience.Entities; +using Elsa.Resilience.Modifiers; +using Elsa.Resilience.Options; +using Elsa.Resilience.Recorders; +using Elsa.Resilience.Serialization; +using Elsa.Resilience.StrategySources; +using Elsa.Workflows; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Resilience.ShellFeatures; + +[ShellFeature] +public class ResilienceShellFeature : IFastEndpointsShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.AddTypeAlias>("RetryAttemptRecordList"); + }); + + services.AddOptions(); + + services + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton(VoidRetryAttemptRecorder.Instance) + .AddSingleton(VoidRetryAttemptReader.Instance) + .AddScoped() + .AddScoped() + .AddScoped() + .AddHandlersFrom(); + + // Register transient exception detection infrastructure + services + .AddSingleton() + .AddSingleton(); + } +} \ No newline at end of file From 32965238f2e6f099c4230fd0684ca599611f4df0 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 9 Feb 2026 20:56:50 +0100 Subject: [PATCH 34/36] Add new shell features: Alterations, Blob Storage, Caching, Clustering, CSharp, Distributed Runtime, ElsaScript, Flowchart, HTTP, JavaScript, Key-Value, and Labels --- .../ShellFeatures/AlterationsFeature.cs | 56 +++++ .../ShellFeatures/ElsaScriptFeature.cs | 28 +++ .../ShellFeatures/CSharpFeature.cs | 46 ++++ .../JavaScriptLibraryFeatures.cs | 76 +++++++ .../ShellFeatures/JavaScriptFeature.cs | 71 ++++++ .../ShellFeatures/LiquidFeature.cs | 50 +++++ .../ShellFeatures/PythonFeature.cs | 50 +++++ .../ShellFeatures/ClusteringFeature.cs | 42 ++++ .../ShellFeatures/HttpCacheFeature.cs | 28 +++ .../Elsa.Http/ShellFeatures/HttpFeature.cs | 211 ++++++++++++++++++ .../ShellFeatures/HttpJavaScriptFeature.cs | 23 ++ .../ShellFeatures/KeyValueFeature.cs | 27 +++ .../ShellFeatures/LabelsFeature.cs | 42 ++++ .../ShellFeatures/SchedulingFeature.cs | 55 +++++ .../MultitenantHttpRoutingFeature.cs | 40 ++++ .../TenantManagementEndpointsFeature.cs | 21 ++ .../ShellFeatures/TenantManagementFeature.cs | 30 +++ .../ShellFeatures/TenantsFeature.cs | 27 +++ .../ElsaScriptBlobStorageFeature.cs | 25 +++ .../ShellFeatures/BlobStorageFeature.cs | 50 +++++ .../RealTimeWorkflowUpdatesFeature.cs | 24 ++ .../ShellFeatures/FlowchartFeature.cs | 35 +++ .../CachingWorkflowDefinitionsFeature.cs | 28 +++ .../DistributedRuntimeFeature.cs | 26 +++ .../CachingWorkflowRuntimeFeature.cs | 31 +++ 25 files changed, 1142 insertions(+) create mode 100644 src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs create mode 100644 src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs create mode 100644 src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs create mode 100644 src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs create mode 100644 src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs create mode 100644 src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs create mode 100644 src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs create mode 100644 src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs create mode 100644 src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs create mode 100644 src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs create mode 100644 src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs create mode 100644 src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs create mode 100644 src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs create mode 100644 src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs create mode 100644 src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs create mode 100644 src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs create mode 100644 src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs create mode 100644 src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs create mode 100644 src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs create mode 100644 src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs create mode 100644 src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs create mode 100644 src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs create mode 100644 src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs create mode 100644 src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs create mode 100644 src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs diff --git a/src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs b/src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs new file mode 100644 index 0000000000..cb8674b42f --- /dev/null +++ b/src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs @@ -0,0 +1,56 @@ +using CShells.Features; +using Elsa.Alterations.Core.Contracts; +using Elsa.Alterations.Core.Entities; +using Elsa.Alterations.Core.Extensions; +using Elsa.Alterations.Core.Stores; +using Elsa.Alterations.Extensions; +using Elsa.Alterations.Services; +using Elsa.Extensions; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Alterations.ShellFeatures; + +/// +/// Adds the Elsa alterations services. +/// +[ShellFeature( + DisplayName = "Alterations", + Description = "Provides workflow alteration capabilities for modifying running workflow instances")] +[UsedImplicitly] +public class AlterationsFeature : IShellFeature +{ + /// + /// Gets or sets the factory for the alteration plan store. + /// + public Func AlterationPlanStoreFactory { get; set; } = sp => sp.GetRequiredService(); + + /// + /// Gets or sets the factory for the alteration job store. + /// + public Func AlterationJobStoreFactory { get; set; } = sp => sp.GetRequiredService(); + + /// + /// Gets or sets the factory for the alteration job dispatcher. + /// + public Func AlterationJobDispatcherFactory { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddAlterations(); + services.AddAlterationsCore(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddMemoryStore(); + services.AddMemoryStore(); + + services.AddScoped(AlterationPlanStoreFactory); + services.AddScoped(AlterationJobStoreFactory); + services.AddScoped(AlterationJobDispatcherFactory); + } +} + diff --git a/src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs b/src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs new file mode 100644 index 0000000000..7d3959f677 --- /dev/null +++ b/src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs @@ -0,0 +1,28 @@ +using CShells.Features; +using Elsa.Dsl.ElsaScript.Compiler; +using Elsa.Dsl.ElsaScript.Contracts; +using Elsa.Dsl.ElsaScript.Materializers; +using Elsa.Dsl.ElsaScript.Parser; +using Elsa.Workflows.Management; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Dsl.ElsaScript.ShellFeatures; + +/// +/// Feature for ElsaScript DSL support. +/// +[ShellFeature( + DisplayName = "ElsaScript DSL", + Description = "Provides ElsaScript DSL support for defining workflows")] +[UsedImplicitly] +public class ElsaScriptFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + } +} + diff --git a/src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs b/src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs new file mode 100644 index 0000000000..04a8e34fbf --- /dev/null +++ b/src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs @@ -0,0 +1,46 @@ +using CShells.Features; +using Elsa.Expressions.CSharp.Activities; +using Elsa.Expressions.CSharp.Contracts; +using Elsa.Expressions.CSharp.Options; +using Elsa.Expressions.CSharp.Providers; +using Elsa.Expressions.CSharp.Services; +using Elsa.Extensions; +using Elsa.Workflows; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.CSharp.ShellFeatures; + +/// +/// Installs C# integration. +/// +[ShellFeature( + DisplayName = "C# Expressions", + Description = "Provides C# expression evaluation capabilities for workflows", + DependsOn = ["Mediator", "Expressions", "MemoryCache"])] +[UsedImplicitly] +public class CSharpFeature : IShellFeature +{ + /// + /// Configures the . + /// + public Action CSharpOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(CSharpOptions); + + // C# services. + services + .AddExpressionDescriptorProvider() + .AddScoped(); + + // Handlers. + services.AddNotificationHandlersFrom(); + + // UI property handlers. + services.AddScoped(); + } +} + + diff --git a/src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs b/src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs new file mode 100644 index 0000000000..c62bdc6d59 --- /dev/null +++ b/src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs @@ -0,0 +1,76 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Expressions.JavaScript.Options; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.JavaScript.Libraries.ShellFeatures; + +/// +/// Base class for JavaScript library shell features. +/// +public abstract class ScriptModuleShellFeatureBase : IShellFeature +{ + private readonly string _moduleName; + + protected ScriptModuleShellFeatureBase(string moduleName) + { + _moduleName = moduleName; + } + + public void ConfigureServices(IServiceCollection services) + { + var moduleName = _moduleName; + services.Configure(options => + { + options.ConfigureEngine((engine, context) => + { + var resourceName = $"Elsa.Expressions.JavaScript.Libraries.ClientLib.dist.{moduleName}.js"; + using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)!; + using var reader = new StreamReader(stream); + var script = reader.ReadToEnd(); + engine.Execute(script); + }); + }); + } +} + +/// +/// Adds Lodash library support to JavaScript expressions. +/// +[ShellFeature( + DisplayName = "Lodash JavaScript Library", + Description = "Provides Lodash utility library for JavaScript expressions", + DependsOn = ["JavaScript"])] +[UsedImplicitly] +public class LodashFeature : ScriptModuleShellFeatureBase +{ + public LodashFeature() : base("lodash") { } +} + +/// +/// Adds Lodash FP library support to JavaScript expressions. +/// +[ShellFeature( + DisplayName = "Lodash FP JavaScript Library", + Description = "Provides Lodash FP (functional programming) utility library for JavaScript expressions", + DependsOn = ["JavaScript"])] +[UsedImplicitly] +public class LodashFpFeature : ScriptModuleShellFeatureBase +{ + public LodashFpFeature() : base("lodashFp") { } +} + +/// +/// Adds Moment.js library support to JavaScript expressions. +/// +[ShellFeature( + DisplayName = "Moment JavaScript Library", + Description = "Provides Moment.js date/time library for JavaScript expressions", + DependsOn = ["JavaScript"])] +[UsedImplicitly] +public class MomentFeature : ScriptModuleShellFeatureBase +{ + public MomentFeature() : base("moment") { } +} + diff --git a/src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs b/src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs new file mode 100644 index 0000000000..4f06cffef9 --- /dev/null +++ b/src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs @@ -0,0 +1,71 @@ +using CShells.Features; +using Elsa.Expressions.JavaScript.Activities; +using Elsa.Expressions.JavaScript.Contracts; +using Elsa.Expressions.JavaScript.Extensions; +using Elsa.Expressions.JavaScript.HostedServices; +using Elsa.Expressions.JavaScript.Options; +using Elsa.Expressions.JavaScript.Providers; +using Elsa.Expressions.JavaScript.Services; +using Elsa.Expressions.JavaScript.TypeDefinitions.Contracts; +using Elsa.Expressions.JavaScript.TypeDefinitions.Services; +using Elsa.Extensions; +using Elsa.Workflows; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.JavaScript.ShellFeatures; + +/// +/// Installs JavaScript integration. +/// +[ShellFeature( + DisplayName = "JavaScript Expressions", + Description = "Provides JavaScript expression evaluation capabilities for workflows", + DependsOn = ["Mediator", "Expressions", "MemoryCache"])] +[UsedImplicitly] +public class JavaScriptFeature : IShellFeature +{ + /// + /// Configures the Jint options. + /// + public Action JintOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(JintOptions); + + // JavaScript services. + services + .AddScoped() + .AddScoped() + .AddExpressionDescriptorProvider(); + + // Type definition services. + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddFunctionDefinitionProvider() + .AddFunctionDefinitionProvider() + .AddFunctionDefinitionProvider() + .AddTypeDefinitionProvider() + .AddTypeDefinitionProvider() + .AddTypeDefinitionProvider() + .AddVariableDefinitionProvider(); + + // Handlers. + services.AddNotificationHandlersFrom(); + + // Type Script definitions. + services.AddFunctionDefinitionProvider(); + + // UI property handlers. + services.AddScoped(); + + // Hosted services. + services.AddHostedService(); + } +} + + diff --git a/src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs b/src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs new file mode 100644 index 0000000000..6702aa2d11 --- /dev/null +++ b/src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs @@ -0,0 +1,50 @@ +using CShells.Features; +using Elsa.Expressions.Liquid.Contracts; +using Elsa.Expressions.Liquid.Filters; +using Elsa.Expressions.Liquid.Handlers; +using Elsa.Expressions.Liquid.Options; +using Elsa.Expressions.Liquid.Providers; +using Elsa.Expressions.Liquid.Services; +using Elsa.Extensions; +using Fluid.Filters; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.Liquid.ShellFeatures; + +/// +/// Configures Liquid functionality. +/// +[ShellFeature( + DisplayName = "Liquid Expressions", + Description = "Provides Liquid template expression evaluation capabilities for workflows", + DependsOn = ["MemoryCache", "Mediator", "Expressions"])] +[UsedImplicitly] +public class LiquidFeature : IShellFeature +{ + /// + /// Configures the Fluid options. + /// + public Action FluidOptions { get; set; } = options => + { + options.ConfigureFilters = context => context.Options.Filters + .WithArrayFilters() + .WithStringFilters() + .WithNumberFilters() + .WithMiscFilters(); + }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(FluidOptions); + + services + .AddHandlersFrom() + .AddScoped() + .AddScoped() + .AddExpressionDescriptorProvider() + .AddLiquidFilter("base64") + .AddLiquidFilter("keys"); + } +} + diff --git a/src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs b/src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs new file mode 100644 index 0000000000..7b5b386282 --- /dev/null +++ b/src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs @@ -0,0 +1,50 @@ +using CShells.Features; +using Elsa.Expressions.Python.Activities; +using Elsa.Expressions.Python.Contracts; +using Elsa.Expressions.Python.HostedServices; +using Elsa.Expressions.Python.Options; +using Elsa.Expressions.Python.Providers; +using Elsa.Expressions.Python.Services; +using Elsa.Extensions; +using Elsa.Workflows; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.Python.ShellFeatures; + +/// +/// Installs Python integration. +/// +[ShellFeature( + DisplayName = "Python Expressions", + Description = "Provides Python expression evaluation capabilities for workflows", + DependsOn = ["Mediator", "Expressions"])] +[UsedImplicitly] +public class PythonFeature : IShellFeature +{ + /// + /// Configures the . + /// + public Action PythonOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(PythonOptions); + + // Python services. + services + .AddScoped() + .AddExpressionDescriptorProvider(); + + // Handlers. + services.AddNotificationHandlersFrom(); + + // UI property handlers. + services.AddScoped(); + + // Hosted services. + services.AddHostedService(); + } +} + + diff --git a/src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs b/src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs new file mode 100644 index 0000000000..fc6eae7f46 --- /dev/null +++ b/src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs @@ -0,0 +1,42 @@ +using CShells.Features; +using Elsa.Hosting.Management.Contracts; +using Elsa.Hosting.Management.HostedServices; +using Elsa.Hosting.Management.Options; +using Elsa.Hosting.Management.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Hosting.Management.ShellFeatures; + +/// +/// Installs and configures the clustering feature. +/// +[ShellFeature( + DisplayName = "Clustering", + Description = "Provides clustering and heartbeat capabilities for distributed Elsa deployments")] +[UsedImplicitly] +public class ClusteringFeature : IShellFeature +{ + /// + /// A factory that instantiates an . + /// + public Func InstanceNameProvider { get; set; } = sp => + { + return ActivatorUtilities.CreateInstance(sp); + }; + + /// + /// Represents the options for heartbeat feature. + /// + public Action HeartbeatOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(HeartbeatOptions) + .AddSingleton(InstanceNameProvider) + .AddSingleton() + .AddHostedService() + .AddHostedService(); + } +} + diff --git a/src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs b/src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs new file mode 100644 index 0000000000..beb7d69d2b --- /dev/null +++ b/src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs @@ -0,0 +1,28 @@ +using CShells.Features; +using Elsa.Http.Handlers; +using Elsa.Http.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Http.ShellFeatures; + +/// +/// Installs services related to HTTP caching. +/// +[ShellFeature( + DisplayName = "HTTP Cache", + Description = "Provides HTTP workflow caching for improved performance", + DependsOn = ["Http", "CachingWorkflowDefinitions"])] +[UsedImplicitly] +public class HttpCacheFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .Decorate() + .AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs b/src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs new file mode 100644 index 0000000000..519590aadd --- /dev/null +++ b/src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs @@ -0,0 +1,211 @@ +using CShells.Features; +using Elsa.Expressions.Options; +using Elsa.Extensions; +using Elsa.Http.Bookmarks; +using Elsa.Http.ContentWriters; +using Elsa.Http.DownloadableContentHandlers; +using Elsa.Http.FileCaches; +using Elsa.Http.Handlers; +using Elsa.Http.Options; +using Elsa.Http.Parsers; +using Elsa.Http.PortResolvers; +using Elsa.Http.Selectors; +using Elsa.Http.Services; +using Elsa.Http.Tasks; +using Elsa.Http.TriggerPayloadValidators; +using Elsa.Http.UIHints; +using Elsa.Workflows; +using FluentStorage; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Elsa.Http.ShellFeatures; + +/// +/// Installs services related to HTTP services and activities. +/// +[ShellFeature( + DisplayName = "HTTP", + Description = "Provides HTTP-related activities and services for workflow execution", + DependsOn = ["HttpJavaScript", "Resilience"])] +[UsedImplicitly] +public class HttpFeature : IShellFeature +{ + /// + /// A delegate to configure . + /// + public Action? ConfigureHttpOptions { get; set; } + + /// + /// A delegate to configure . + /// + public Action? ConfigureHttpFileCacheOptions { get; set; } + + /// + /// A delegate that is invoked when authorizing an inbound HTTP request. + /// + public Func HttpEndpointAuthorizationHandler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that is invoked when an HTTP workflow faults. + /// + public Func HttpEndpointWorkflowFaultHandler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate to configure the . + /// + public Func ContentTypeProvider { get; set; } = _ => new FileExtensionContentTypeProvider(); + + /// + /// A delegate to configure the . + /// + public Func FileCache { get; set; } = sp => + { + var options = sp.GetRequiredService>().Value; + var blobStorage = StorageFactory.Blobs.DirectoryFiles(options.LocalCacheDirectory); + return new BlobFileCacheStorageProvider(blobStorage); + }; + + /// + /// A delegate to configure the used when by the and activities. + /// + public Action HttpClient { get; set; } = (_, _) => { }; + + /// + /// A delegate to configure the for . + /// + public Action HttpClientBuilder { get; set; } = _ => { }; + + /// + /// A list of types to register with the service collection. + /// + public ICollection HttpCorrelationIdSelectorTypes { get; } = new List + { + typeof(HeaderHttpCorrelationIdSelector), + typeof(QueryStringHttpCorrelationIdSelector) + }; + + /// + /// A list of types to register with the service collection. + /// + public ICollection HttpWorkflowInstanceIdSelectorTypes { get; } = new List + { + typeof(HeaderHttpWorkflowInstanceIdSelector), + typeof(QueryStringHttpWorkflowInstanceIdSelector) + }; + + public void ConfigureServices(IServiceCollection services) + { + var configureOptions = ConfigureHttpOptions ?? (options => + { + options.BasePath = "/workflows"; + options.BaseUrl = new Uri("http://localhost"); + }); + + var configureFileCacheOptions = ConfigureHttpFileCacheOptions ?? (options => { options.TimeToLive = TimeSpan.FromDays(7); }); + + services.Configure(configureOptions); + services.Configure(configureFileCacheOptions); + + var httpClientBuilder = services.AddHttpClient(HttpClient); + HttpClientBuilder(httpClientBuilder); + + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(ContentTypeProvider) + .AddHttpContextAccessor() + + // Handlers. + .AddNotificationHandler() + + // Content parsers. + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + + // HTTP content factories. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Activity property options providers. + .AddScoped() + .AddScoped() + + // Default providers. + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()) + + // Port resolvers. + .AddScoped() + + // HTTP endpoint handlers. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(HttpEndpointWorkflowFaultHandler) + .AddScoped(HttpEndpointAuthorizationHandler) + .AddScoped(sp => sp.GetRequiredService()) + + // Startup tasks. + .AddStartupTask() + + // Downloadable content handlers. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + //Trigger payload validators. + .AddTriggerPayloadValidator() + + // File caches. + .AddScoped(FileCache) + .AddScoped() + + // AuthenticationBasedHttpEndpointAuthorizationHandler requires Authorization services. + .AddAuthorization(); + + // HTTP clients. + services.AddHttpClient(); + + // Add selectors. + foreach (var httpCorrelationIdSelectorType in HttpCorrelationIdSelectorTypes) + services.AddScoped(typeof(IHttpCorrelationIdSelector), httpCorrelationIdSelectorType); + + foreach (var httpWorkflowInstanceIdSelectorType in HttpWorkflowInstanceIdSelectorTypes) + services.AddScoped(typeof(IHttpWorkflowInstanceIdSelector), httpWorkflowInstanceIdSelectorType); + + services.Configure(options => + { + options.AddTypeAlias("HttpRequest"); + options.AddTypeAlias("HttpResponse"); + options.AddTypeAlias("HttpResponseMessage"); + options.AddTypeAlias("HttpHeaders"); + options.AddTypeAlias("RouteData"); + options.AddTypeAlias("FormFile"); + options.AddTypeAlias("FormFile[]"); + options.AddTypeAlias("HttpFile"); + options.AddTypeAlias("HttpFile[]"); + options.AddTypeAlias("Downloadable"); + options.AddTypeAlias("Downloadable[]"); + }); + } +} + diff --git a/src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs b/src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs new file mode 100644 index 0000000000..05d73b0ebc --- /dev/null +++ b/src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs @@ -0,0 +1,23 @@ +using CShells.Features; +using Elsa.Http.Scripting.JavaScript; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Http.ShellFeatures; + +/// +/// Provides JavaScript integration for HTTP features. +/// +[ShellFeature( + DisplayName = "HTTP JavaScript", + Description = "Provides JavaScript integration for HTTP activities")] +[UsedImplicitly] +public class HttpJavaScriptFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs b/src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs new file mode 100644 index 0000000000..7cbb1ce595 --- /dev/null +++ b/src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs @@ -0,0 +1,27 @@ +using CShells.Features; +using Elsa.KeyValues.Contracts; +using Elsa.KeyValues.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.KeyValues.ShellFeatures; + +/// +/// Installs and configures key-value store features. +/// +[ShellFeature( + DisplayName = "Key-Value Store", + Description = "Provides key-value storage capabilities for workflows")] +[UsedImplicitly] +public class KeyValueFeature : IShellFeature +{ + /// + /// A factory that instantiates an . + /// + public Func KeyValueStore { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(KeyValueStore); + } +} diff --git a/src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs b/src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs new file mode 100644 index 0000000000..2c67715ea1 --- /dev/null +++ b/src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs @@ -0,0 +1,42 @@ +using CShells.Features; +using Elsa.Extensions; +using Elsa.Labels.Contracts; +using Elsa.Labels.Entities; +using Elsa.Labels.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Labels.ShellFeatures; + +/// +/// Enables functionality to tag workflows with labels. +/// +[ShellFeature( + DisplayName = "Labels", + Description = "Enables functionality to tag workflows with labels", + DependsOn = ["Mediator"])] +[UsedImplicitly] +public class LabelsFeature : IShellFeature +{ + /// + /// A delegate that provides an instance of an implementation of . + /// + public Func LabelStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that provides an instance of an implementation of . + /// + public Func WorkflowDefinitionLabelStore { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryStore() + .AddMemoryStore() + .AddScoped(LabelStore) + .AddScoped(WorkflowDefinitionLabelStore); + + services.AddNotificationHandlersFrom(GetType()); + } +} + diff --git a/src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs b/src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs new file mode 100644 index 0000000000..7473a0b116 --- /dev/null +++ b/src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs @@ -0,0 +1,55 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Common.RecurringTasks; +using Elsa.Extensions; +using Elsa.Scheduling.Bookmarks; +using Elsa.Scheduling.Handlers; +using Elsa.Scheduling.HostedServices; +using Elsa.Scheduling.Services; +using Elsa.Scheduling.TriggerPayloadValidators; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Scheduling.ShellFeatures; + +/// +/// Provides scheduling features to the system. +/// +[ShellFeature( + DisplayName = "Scheduling", + Description = "Provides scheduling capabilities for workflows including cron and delay-based triggers", + DependsOn = ["SystemClock"])] +[UsedImplicitly] +public class SchedulingFeature : IShellFeature +{ + /// + /// Gets or sets the trigger scheduler factory. + /// + public Func WorkflowScheduler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// Gets or sets the CRON parser factory. + /// + public Func CronParser { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton() + .AddSingleton() + .AddSingleton(CronParser) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(WorkflowScheduler) + .AddBackgroundTask() + .AddHandlersFrom() + + //Trigger payload validators. + .AddTriggerPayloadValidator(); + } +} + diff --git a/src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs b/src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs new file mode 100644 index 0000000000..139ddf193f --- /dev/null +++ b/src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs @@ -0,0 +1,40 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Tenants.AspNetCore.Options; +using Elsa.Tenants.AspNetCore.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.AspNetCore.ShellFeatures; + +/// +/// Provides multi-tenant HTTP routing features. +/// +[ShellFeature( + DisplayName = "Multi-tenant HTTP Routing", + Description = "Provides multi-tenant HTTP routing capabilities for workflows")] +[UsedImplicitly] +public class MultitenantHttpRoutingFeature : IShellFeature +{ + /// + /// Gets or sets the tenant header name. + /// + public string TenantHeaderName { get; set; } = "X-Tenant-Id"; + + public void ConfigureServices(IServiceCollection services) + { + // Multitenancy HTTP options. + services.Configure(options => + { + options.TenantHeaderName = TenantHeaderName; + }); + + // Tenant resolvers. + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(); + } +} + diff --git a/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs new file mode 100644 index 0000000000..5ad6276085 --- /dev/null +++ b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs @@ -0,0 +1,21 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.ShellFeatures; + +/// +/// Enables tenant management endpoints. +/// +[ShellFeature( + DisplayName = "Tenant Management Endpoints", + Description = "Provides REST API endpoints for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class TenantManagementEndpointsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + // FastEndpoints are registered via assembly scanning + } +} diff --git a/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs new file mode 100644 index 0000000000..e6c1d1e167 --- /dev/null +++ b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs @@ -0,0 +1,30 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Extensions; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.ShellFeatures; + +/// +/// Enables tenant management capabilities. +/// +[ShellFeature( + DisplayName = "Tenant Management", + Description = "Provides tenant store and management capabilities")] +[UsedImplicitly] +public class TenantManagementFeature : IShellFeature +{ + /// + /// A factory that instantiates an . + /// + public Func TenantStoreFactory { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryStore() + .AddScoped(TenantStoreFactory); + } +} + diff --git a/src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs b/src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs new file mode 100644 index 0000000000..435d0ddf6c --- /dev/null +++ b/src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs @@ -0,0 +1,27 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Tenants.Mediator.Tasks; +using Elsa.Tenants.Options; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.ShellFeatures; + +/// +/// Configures multi-tenancy features. +/// +[ShellFeature( + DisplayName = "Tenants", + Description = "Provides multi-tenancy capabilities for workflows")] +[UsedImplicitly] +public class TenantsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddHostedService() + .AddScoped() + .AddScoped(); + } +} + diff --git a/src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs b/src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs new file mode 100644 index 0000000000..773df8e825 --- /dev/null +++ b/src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.WorkflowProviders.BlobStorage.Contracts; +using Elsa.WorkflowProviders.BlobStorage.ElsaScript.Handlers; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.WorkflowProviders.BlobStorage.ElsaScript.ShellFeatures; + +/// +/// A feature that enables ElsaScript support for the BlobStorage workflow provider. +/// +[ShellFeature( + DisplayName = "ElsaScript Blob Storage", + Description = "Provides ElsaScript format support for the BlobStorage workflow provider", + DependsOn = ["BlobStorage", "ElsaScript"])] +[UsedImplicitly] +public class ElsaScriptBlobStorageFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + // Register the ElsaScript format handler + services.AddScoped(); + } +} + diff --git a/src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs b/src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs new file mode 100644 index 0000000000..805f89aba5 --- /dev/null +++ b/src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs @@ -0,0 +1,50 @@ +using System.Reflection; +using CShells.Features; +using Elsa.WorkflowProviders.BlobStorage.Contracts; +using Elsa.WorkflowProviders.BlobStorage.Handlers; +using Elsa.WorkflowProviders.BlobStorage.Providers; +using Elsa.Workflows.Runtime; +using FluentStorage; +using FluentStorage.Blobs; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.WorkflowProviders.BlobStorage.ShellFeatures; + +/// +/// A feature that enables the FluentStorage workflow definition provider. +/// +[ShellFeature( + DisplayName = "Blob Storage Workflow Provider", + Description = "Provides workflow definitions from blob storage", + DependsOn = ["WorkflowManagement"])] +[UsedImplicitly] +public class BlobStorageFeature : IShellFeature +{ + /// + /// The blob storage to use. + /// + public Func BlobStorage { get; set; } = _ => StorageFactory.Blobs.DirectoryFiles(GetDefaultWorkflowsDirectory()); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(sp => new BlobStorageProvider(BlobStorage(sp))); + + // Register the JSON format handler (built-in support) + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + } + + /// + /// Gets the default workflows directory. + /// + public static string GetDefaultWorkflowsDirectory() + { + var entryAssemblyDir = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!; + var directory = Path.Combine(entryAssemblyDir, "Workflows"); + return directory; + } +} + diff --git a/src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs b/src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs new file mode 100644 index 0000000000..9ec781fab2 --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs @@ -0,0 +1,24 @@ +using CShells.Features; +using Elsa.Workflows.Api.RealTime.Handlers; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Api.ShellFeatures; + +/// +/// Sets up a SignalR hub for receiving workflow events on the client. +/// +[ShellFeature( + DisplayName = "Real-Time Workflow Updates", + Description = "Provides real-time workflow updates via SignalR")] +[UsedImplicitly] +public class RealTimeWorkflowUpdatesFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSignalR(); + services.AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs new file mode 100644 index 0000000000..b7d9fdce30 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs @@ -0,0 +1,35 @@ +using CShells.Features; +using Elsa.Extensions; +using Elsa.Workflows.Activities.Flowchart.Options; +using Elsa.Workflows.Activities.Flowchart.Serialization; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.ShellFeatures; + +/// +/// Adds support for the Flowchart activity. +/// +[ShellFeature( + DisplayName = "Flowchart", + Description = "Adds support for the Flowchart activity")] +[UsedImplicitly] +public class FlowchartFeature : IShellFeature +{ + /// + /// A delegate to configure . + /// + public Action? FlowchartOptionsConfigurator { get; set; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddSerializationOptionsConfigurator(); + + // Register FlowchartOptions + services.AddOptions(); + + if (FlowchartOptionsConfigurator != null) + services.Configure(FlowchartOptionsConfigurator); + } +} + diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs new file mode 100644 index 0000000000..6e42d111b8 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs @@ -0,0 +1,28 @@ +using CShells.Features; +using Elsa.Workflows.Management.Handlers.Notifications; +using Elsa.Workflows.Management.Services; +using Elsa.Workflows.Management.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Configures workflow definition caching. +/// +[ShellFeature( + DisplayName = "Caching Workflow Definitions", + Description = "Provides caching for workflow definitions")] +[UsedImplicitly] +public class CachingWorkflowDefinitionsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.Decorate(); + services.Decorate(); + services.AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs new file mode 100644 index 0000000000..5f2a401893 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs @@ -0,0 +1,26 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Runtime.Distributed.ShellFeatures; + +/// +/// Installs and configures distributed workflow runtime features. +/// +[ShellFeature( + DisplayName = "Distributed Runtime", + Description = "Provides distributed workflow runtime capabilities", + DependsOn = ["WorkflowRuntime", "Resilience"])] +[UsedImplicitly] +public class DistributedRuntimeFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()) + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()); + } +} + diff --git a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs new file mode 100644 index 0000000000..90949395e3 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs @@ -0,0 +1,31 @@ +using CShells.Features; +using Elsa.Workflows.Runtime.Handlers; +using Elsa.Workflows.Runtime.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Runtime.ShellFeatures; + +/// +/// Installs and configures workflow runtime caching features. +/// +[ShellFeature( + DisplayName = "Caching Workflow Runtime", + Description = "Provides caching for workflow runtime operations", + DependsOn = ["MemoryCache"])] +[UsedImplicitly] +public class CachingWorkflowRuntimeFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + // Decorators. + .Decorate() + + // Handlers. + .AddNotificationHandler() + .AddNotificationHandler(); + } +} + + From d750cd6feda67daf279c3eaa638e3033ee040b08 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 9 Feb 2026 21:09:31 +0100 Subject: [PATCH 35/36] Switch project references to package references for CShells libraries and update to version 0.0.7. --- Directory.Packages.props | 11 ++-- Elsa.sln | 55 ------------------- .../Elsa.ModularServer.Web.csproj | 9 ++- .../Elsa.Api.Common/Elsa.Api.Common.csproj | 2 - src/common/Elsa.Features/Elsa.Features.csproj | 6 +- .../Elsa.Identity/Elsa.Identity.csproj | 2 +- .../Elsa.Workflows.Api.csproj | 5 +- 7 files changed, 16 insertions(+), 74 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index b32a41fa11..8709d39471 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -100,11 +100,12 @@ - - - - - + + + + + + diff --git a/Elsa.sln b/Elsa.sln index f4b9600e1d..c043709eed 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -329,22 +329,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dsl", "dsl", "{477C2416-312 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.ModularServer.Web", "src\apps\Elsa.ModularServer.Web\Elsa.ModularServer.Web.csproj", "{E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cshells", "cshells", "{71595D88-A05C-446E-B294-5CB6D91FB91F}" - ProjectSection(SolutionItems) = preProject - ..\..\..\sfmskywalker\cshells\main\Directory.Packages.props = ..\..\..\sfmskywalker\cshells\main\Directory.Packages.props - ..\..\..\sfmskywalker\cshells\main\Directory.Build.props = ..\..\..\sfmskywalker\cshells\main\Directory.Build.props - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells", "..\..\..\sfmskywalker\cshells\main\src\CShells\CShells.csproj", "{A70C589D-A498-4139-9CA9-CA82CB7864F0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.Abstractions", "..\..\..\sfmskywalker\cshells\main\src\CShells.Abstractions\CShells.Abstractions.csproj", "{E06268B5-C087-4508-86A7-8D8482759A0D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.AspNetCore.Abstractions", "..\..\..\sfmskywalker\cshells\main\src\CShells.AspNetCore.Abstractions\CShells.AspNetCore.Abstractions.csproj", "{4096D2AB-1C9D-40B9-9F5C-7235089759A5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.Providers.FluentStorage", "..\..\..\sfmskywalker\cshells\main\src\CShells.Providers.FluentStorage\CShells.Providers.FluentStorage.csproj", "{E8397C32-5BD2-4DE8-BB29-96624A9C75A0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.AspNetCore", "..\..\..\sfmskywalker\cshells\main\src\CShells.AspNetCore\CShells.AspNetCore.csproj", "{8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Resilience.Core.UnitTests", "test\unit\Elsa.Resilience.Core.UnitTests\Elsa.Resilience.Core.UnitTests.csproj", "{B8006D70-1630-43DB-A043-FA89FAC70F37}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Common.UnitTests", "test\unit\Elsa.Common.UnitTests\Elsa.Common.UnitTests.csproj", "{A3C07D5B-2A30-494E-B9BC-4B1594B31ABC}" @@ -353,10 +337,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Http.IntegrationTests" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Tenants.UnitTests", "test\unit\Elsa.Tenants.UnitTests\Elsa.Tenants.UnitTests.csproj", "{DC476900-D836-4920-A696-CF8796668723}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.FastEndpoints.Abstractions", "..\..\..\sfmskywalker\cshells\main\src\CShells.FastEndpoints.Abstractions\CShells.FastEndpoints.Abstractions.csproj", "{BE744448-F7F9-4807-BA5B-47D3463E4CF6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CShells.FastEndpoints", "..\..\..\sfmskywalker\cshells\main\src\CShells.FastEndpoints\CShells.FastEndpoints.csproj", "{690B7E4E-62C1-409B-BB9B-F8F55051CA27}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -617,26 +597,6 @@ Global {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Release|Any CPU.Build.0 = Release|Any CPU - {A70C589D-A498-4139-9CA9-CA82CB7864F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A70C589D-A498-4139-9CA9-CA82CB7864F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A70C589D-A498-4139-9CA9-CA82CB7864F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A70C589D-A498-4139-9CA9-CA82CB7864F0}.Release|Any CPU.Build.0 = Release|Any CPU - {E06268B5-C087-4508-86A7-8D8482759A0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E06268B5-C087-4508-86A7-8D8482759A0D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E06268B5-C087-4508-86A7-8D8482759A0D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E06268B5-C087-4508-86A7-8D8482759A0D}.Release|Any CPU.Build.0 = Release|Any CPU - {4096D2AB-1C9D-40B9-9F5C-7235089759A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4096D2AB-1C9D-40B9-9F5C-7235089759A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4096D2AB-1C9D-40B9-9F5C-7235089759A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4096D2AB-1C9D-40B9-9F5C-7235089759A5}.Release|Any CPU.Build.0 = Release|Any CPU - {E8397C32-5BD2-4DE8-BB29-96624A9C75A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8397C32-5BD2-4DE8-BB29-96624A9C75A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8397C32-5BD2-4DE8-BB29-96624A9C75A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8397C32-5BD2-4DE8-BB29-96624A9C75A0}.Release|Any CPU.Build.0 = Release|Any CPU - {8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7}.Release|Any CPU.Build.0 = Release|Any CPU {B8006D70-1630-43DB-A043-FA89FAC70F37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B8006D70-1630-43DB-A043-FA89FAC70F37}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8006D70-1630-43DB-A043-FA89FAC70F37}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -653,14 +613,6 @@ Global {DC476900-D836-4920-A696-CF8796668723}.Debug|Any CPU.Build.0 = Debug|Any CPU {DC476900-D836-4920-A696-CF8796668723}.Release|Any CPU.ActiveCfg = Release|Any CPU {DC476900-D836-4920-A696-CF8796668723}.Release|Any CPU.Build.0 = Release|Any CPU - {BE744448-F7F9-4807-BA5B-47D3463E4CF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BE744448-F7F9-4807-BA5B-47D3463E4CF6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BE744448-F7F9-4807-BA5B-47D3463E4CF6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BE744448-F7F9-4807-BA5B-47D3463E4CF6}.Release|Any CPU.Build.0 = Release|Any CPU - {690B7E4E-62C1-409B-BB9B-F8F55051CA27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {690B7E4E-62C1-409B-BB9B-F8F55051CA27}.Debug|Any CPU.Build.0 = Debug|Any CPU - {690B7E4E-62C1-409B-BB9B-F8F55051CA27}.Release|Any CPU.ActiveCfg = Release|Any CPU - {690B7E4E-62C1-409B-BB9B-F8F55051CA27}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -763,17 +715,10 @@ Global {477C2416-312D-46AE-BCD6-8FA1FAB43624} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79} {874F5A44-DB06-47AB-A18C-2D13942E0147} = {477C2416-312D-46AE-BCD6-8FA1FAB43624} {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC} = {D92BEAB2-60D6-4BB4-885A-6BA681C6CCF1} - {A70C589D-A498-4139-9CA9-CA82CB7864F0} = {71595D88-A05C-446E-B294-5CB6D91FB91F} - {E06268B5-C087-4508-86A7-8D8482759A0D} = {71595D88-A05C-446E-B294-5CB6D91FB91F} - {4096D2AB-1C9D-40B9-9F5C-7235089759A5} = {71595D88-A05C-446E-B294-5CB6D91FB91F} - {E8397C32-5BD2-4DE8-BB29-96624A9C75A0} = {71595D88-A05C-446E-B294-5CB6D91FB91F} - {8A645D0A-F4F6-4A7E-B917-0FFCB66C50B7} = {71595D88-A05C-446E-B294-5CB6D91FB91F} {B8006D70-1630-43DB-A043-FA89FAC70F37} = {18453B51-25EB-4317-A4B3-B10518252E92} {A3C07D5B-2A30-494E-B9BC-4B1594B31ABC} = {18453B51-25EB-4317-A4B3-B10518252E92} {8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E} = {1B8D5897-902E-4632-8698-E89CAF3DDF54} {DC476900-D836-4920-A696-CF8796668723} = {18453B51-25EB-4317-A4B3-B10518252E92} - {BE744448-F7F9-4807-BA5B-47D3463E4CF6} = {71595D88-A05C-446E-B294-5CB6D91FB91F} - {690B7E4E-62C1-409B-BB9B-F8F55051CA27} = {71595D88-A05C-446E-B294-5CB6D91FB91F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E} diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj index b91683106f..d2f791b735 100644 --- a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -1,10 +1,9 @@ - - - - - + + + + diff --git a/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj b/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj index b1d3a9df7b..ff63d15173 100644 --- a/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj +++ b/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj @@ -17,8 +17,6 @@ - - diff --git a/src/common/Elsa.Features/Elsa.Features.csproj b/src/common/Elsa.Features/Elsa.Features.csproj index f9ea3d9b30..1f20784103 100644 --- a/src/common/Elsa.Features/Elsa.Features.csproj +++ b/src/common/Elsa.Features/Elsa.Features.csproj @@ -8,13 +8,9 @@ - + - - - - diff --git a/src/modules/Elsa.Identity/Elsa.Identity.csproj b/src/modules/Elsa.Identity/Elsa.Identity.csproj index 33ab4e5906..76ecf8edc2 100644 --- a/src/modules/Elsa.Identity/Elsa.Identity.csproj +++ b/src/modules/Elsa.Identity/Elsa.Identity.csproj @@ -9,10 +9,10 @@ + - diff --git a/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj b/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj index a0f05c1465..f36f1fdcce 100644 --- a/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj +++ b/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj @@ -6,9 +6,12 @@ elsa module workflows api + + + + - From 2e931a725bc5382b92b1ea5be7115e113953f1bc Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 9 Feb 2026 21:18:02 +0100 Subject: [PATCH 36/36] Potential fix for pull request finding 'Call to 'System.IO.Path.Combine' may silently drop its earlier arguments' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> --- .../ShellFeatures/WorkflowRuntimeFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs index c3276cf81c..0fdc8ae6ea 100644 --- a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs +++ b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs @@ -88,7 +88,7 @@ public class WorkflowRuntimeFeature : IShellFeature /// /// A factory that instantiates an . /// - public Func DistributedLockProvider { get; set; } = _ => new FileDistributedSynchronizationProvider(new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "App_Data/locks"))); + public Func DistributedLockProvider { get; set; } = _ => new FileDistributedSynchronizationProvider(new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "App_Data", "locks"))); /// /// A factory that instantiates an .