diff --git a/Directory.Packages.props b/Directory.Packages.props
index 70abc011d0..452482edcc 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,171 +1,171 @@
-
-
- true
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Elsa.sln b/Elsa.sln
index f03016a978..0cccd0d7a6 100644
--- a/Elsa.sln
+++ b/Elsa.sln
@@ -1,4 +1,3 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34003.232
@@ -277,6 +276,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.FileStorage", "src\mod
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "storage", "storage", "{B818988E-639C-4E6E-85C1-B231BCAD9DAB}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Tenants", "src\modules\Elsa.Tenants\Elsa.Tenants.csproj", "{29638A67-E79F-44FE-AC05-DA499EBA929E}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.CSharp", "src\modules\Elsa.CSharp\Elsa.CSharp.csproj", "{24331E82-D7AF-45B1-ACF0-CA6C3B0B77DC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Python", "src\modules\Elsa.Python\Elsa.Python.csproj", "{790E94F2-5393-47DF-AC52-D9247F5B243A}"
@@ -295,26 +296,26 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pipelines", "pipelines", "{
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{99F2B1DA-2F69-4D70-A2A3-AC985AD91EC4}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.AspNet.BatchProcessing", "samples\aspnet\Elsa.Samples.AspNet.BatchProcessing\Elsa.Samples.AspNet.BatchProcessing.csproj", "{0AAF5EF6-02E5-44F9-B2CB-B1401FC5EF66}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.AspNet.BatchProcessing", "samples\aspnet\Elsa.Samples.AspNet.BatchProcessing\Elsa.Samples.AspNet.BatchProcessing.csproj", "{0AAF5EF6-02E5-44F9-B2CB-B1401FC5EF66}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Studio.Web", "src\bundles\Elsa.Studio.Web\Elsa.Studio.Web.csproj", "{F0385327-CEDE-4CD4-88A5-B66E73E75B28}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Studio.Web", "src\bundles\Elsa.Studio.Web\Elsa.Studio.Web.csproj", "{F0385327-CEDE-4CD4-88A5-B66E73E75B28}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.AspNet.CustomUIHandler", "samples\aspnet\Elsa.Samples.AspNet.CustomUIHandler\Elsa.Samples.AspNet.CustomUIHandler.csproj", "{E4BF9791-4086-41EB-8EF0-02686A5F3F65}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.AspNet.CustomUIHandler", "samples\aspnet\Elsa.Samples.AspNet.CustomUIHandler\Elsa.Samples.AspNet.CustomUIHandler.csproj", "{E4BF9791-4086-41EB-8EF0-02686A5F3F65}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.AspNet.Heartbeats", "samples\aspnet\Elsa.Samples.AspNet.Heartbeats\Elsa.Samples.AspNet.Heartbeats.csproj", "{73852FEC-9847-4C6C-B1F5-1BB014C50A79}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.AspNet.Heartbeats", "samples\aspnet\Elsa.Samples.AspNet.Heartbeats\Elsa.Samples.AspNet.Heartbeats.csproj", "{73852FEC-9847-4C6C-B1F5-1BB014C50A79}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.MassTransit.AzureServiceBus", "src\modules\Elsa.MassTransit.AzureServiceBus\Elsa.MassTransit.AzureServiceBus.csproj", "{AFEB799E-82C3-4D02-9D5C-766BB8DEF004}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.MassTransit.AzureServiceBus", "src\modules\Elsa.MassTransit.AzureServiceBus\Elsa.MassTransit.AzureServiceBus.csproj", "{AFEB799E-82C3-4D02-9D5C-766BB8DEF004}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "migrations", "migrations", "{C80C8231-D35C-4ACC-9ED6-9F3DB221535E}"
ProjectSection(SolutionItems) = preProject
- migrations\efcore-3.1.sh = migrations\efcore-3.1.sh
- migrations\efcore-3.0.sh = migrations\efcore-3.0.sh
- migrations\efcore-3.1-sql.sh = migrations\efcore-3.1-sql.sh
migrations\README.md = migrations\README.md
+ migrations\efcore-3.1.sh = migrations\efcore-3.1.sh
migrations\efcore-3.2.sh = migrations\efcore-3.2.sh
+ migrations\efcore-3.0.sh = migrations\efcore-3.0.sh
+ migrations\efcore-3.3.sh = migrations\efcore-3.3.sh
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Alterations.IntegrationTests", "test\integration\Elsa.Alterations.IntegrationTests\Elsa.Alterations.IntegrationTests.csproj", "{F50336DA-42D1-4DD1-A107-67AFEB8A33EE}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Alterations.IntegrationTests", "test\integration\Elsa.Alterations.IntegrationTests\Elsa.Alterations.IntegrationTests.csproj", "{F50336DA-42D1-4DD1-A107-67AFEB8A33EE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "hosting", "hosting", "{A516931E-EDBB-4FC3-BB94-1BB824D5BC61}"
EndProject
@@ -359,6 +360,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.JavaScript.Integration
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.AzureServiceBus.ComponentTests", "test\component\Elsa.AzureServiceBus.ComponentTests\Elsa.AzureServiceBus.ComponentTests.csproj", "{7F5A304F-7DD9-4A64-8FCD-9B1FF06735B9}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.AspNet.Tenants", "samples\aspnet\Elsa.Samples.AspNet.Tenants\Elsa.Samples.AspNet.Tenants.csproj", "{9A6395A7-9F82-4F82-BFCB-1EBC38F0A86D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.AspNet.Tenants.External", "samples\aspnet\Elsa.Samples.AspNet.Tenants.External\Elsa.Samples.AspNet.Tenants.External.csproj", "{1B9E55EB-4379-4B95-9F84-EEEB5AA20987}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -745,6 +750,10 @@ Global
{732BF088-6AD7-4C4D-9A48-8074253596D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{732BF088-6AD7-4C4D-9A48-8074253596D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{732BF088-6AD7-4C4D-9A48-8074253596D4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {29638A67-E79F-44FE-AC05-DA499EBA929E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {29638A67-E79F-44FE-AC05-DA499EBA929E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {29638A67-E79F-44FE-AC05-DA499EBA929E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {29638A67-E79F-44FE-AC05-DA499EBA929E}.Release|Any CPU.Build.0 = Release|Any CPU
{24331E82-D7AF-45B1-ACF0-CA6C3B0B77DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{24331E82-D7AF-45B1-ACF0-CA6C3B0B77DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24331E82-D7AF-45B1-ACF0-CA6C3B0B77DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -757,6 +766,8 @@ Global
{169BEA3D-2A81-47EE-A6C1-3F8719EEC1F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{169BEA3D-2A81-47EE-A6C1-3F8719EEC1F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{169BEA3D-2A81-47EE-A6C1-3F8719EEC1F6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {99F2B1DA-2F69-4D70-A2A3-AC985AD91EC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {99F2B1DA-2F69-4D70-A2A3-AC985AD91EC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0AAF5EF6-02E5-44F9-B2CB-B1401FC5EF66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0AAF5EF6-02E5-44F9-B2CB-B1401FC5EF66}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0AAF5EF6-02E5-44F9-B2CB-B1401FC5EF66}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -781,8 +792,6 @@ Global
{F50336DA-42D1-4DD1-A107-67AFEB8A33EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F50336DA-42D1-4DD1-A107-67AFEB8A33EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F50336DA-42D1-4DD1-A107-67AFEB8A33EE}.Release|Any CPU.Build.0 = Release|Any CPU
- {99F2B1DA-2F69-4D70-A2A3-AC985AD91EC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {99F2B1DA-2F69-4D70-A2A3-AC985AD91EC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BBCE36D1-6767-4ED1-B3E8-84D2567A962A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BBCE36D1-6767-4ED1-B3E8-84D2567A962A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BBCE36D1-6767-4ED1-B3E8-84D2567A962A}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -855,6 +864,14 @@ Global
{7F5A304F-7DD9-4A64-8FCD-9B1FF06735B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F5A304F-7DD9-4A64-8FCD-9B1FF06735B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F5A304F-7DD9-4A64-8FCD-9B1FF06735B9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9A6395A7-9F82-4F82-BFCB-1EBC38F0A86D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9A6395A7-9F82-4F82-BFCB-1EBC38F0A86D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9A6395A7-9F82-4F82-BFCB-1EBC38F0A86D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9A6395A7-9F82-4F82-BFCB-1EBC38F0A86D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1B9E55EB-4379-4B95-9F84-EEEB5AA20987}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1B9E55EB-4379-4B95-9F84-EEEB5AA20987}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1B9E55EB-4379-4B95-9F84-EEEB5AA20987}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1B9E55EB-4379-4B95-9F84-EEEB5AA20987}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -974,6 +991,7 @@ Global
{CCCCEF8C-7D96-4BEA-B9D0-E91EDF08E65D} = {AB797AF0-C12C-46DE-A157-7E25625C6200}
{732BF088-6AD7-4C4D-9A48-8074253596D4} = {B818988E-639C-4E6E-85C1-B231BCAD9DAB}
{B818988E-639C-4E6E-85C1-B231BCAD9DAB} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
+ {29638A67-E79F-44FE-AC05-DA499EBA929E} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
{24331E82-D7AF-45B1-ACF0-CA6C3B0B77DC} = {6EF07978-A6D2-40EB-891D-7D70C5F37E76}
{790E94F2-5393-47DF-AC52-D9247F5B243A} = {6EF07978-A6D2-40EB-891D-7D70C5F37E76}
{169BEA3D-2A81-47EE-A6C1-3F8719EEC1F6} = {DD089B8B-DA73-492A-9010-F772D1C178DA}
@@ -1004,6 +1022,8 @@ Global
{99B171E6-0248-4402-836D-98947CD63772} = {1B8D5897-902E-4632-8698-E89CAF3DDF54}
{4332A6BC-434A-4AF5-A075-F1BBCDD28F5D} = {1B8D5897-902E-4632-8698-E89CAF3DDF54}
{7F5A304F-7DD9-4A64-8FCD-9B1FF06735B9} = {08B41FFA-CEE3-46A7-B5C0-3EB65D37A16C}
+ {9A6395A7-9F82-4F82-BFCB-1EBC38F0A86D} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
+ {1B9E55EB-4379-4B95-9F84-EEEB5AA20987} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E}
diff --git a/generate-migrations-initial copy.sh b/generate-migrations-initial copy.sh
deleted file mode 100644
index f5dacadae2..0000000000
--- a/generate-migrations-initial copy.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env zsh
-
-# Define the modules to update
-mods=("Management")
-# mods=("Alterations" "Runtime" "Management" "Identity" "Labels")
-
-# Define the list of providers
-providers=("MySql" "SqlServer" "Sqlite" "PostgreSql")
-# providers=("SqlServer")
-
-# Connection strings for each provider
-typeset -A connStrings
-connStrings=(
- MySql "Server=localhost;Port=3306;Database=elsa;User=root;Password=password;"
- SqlServer ""
- Sqlite ""
- PostgreSql ""
-)
-
-# Loop through each module
-for module in "${mods[@]}"; do
- # Loop through each provider
- for provider in "${providers[@]}"; do
- providerPath="./src/modules/Elsa.EntityFrameworkCore.$provider"
- migrationsPath="Migrations/$module"
-
- echo "Updating migrations for $provider..."
- echo "Provider path: ${providerPath:?}/${migrationsPath}"
- echo "Migrations path: $migrationsPath"
- echo "Connection string: ${connStrings[$provider]}"
-
- # 1. Delete the existing migrations folder
- rm -rf "${providerPath:?}/${migrationsPath}"
-
- # 2. Run the migrations command
- dotnet ef migrations add Initial -c "$module"ElsaDbContext -p "$providerPath" -o "$migrationsPath" -- --connectionString "${connStrings[$provider]}"
- done
-done
diff --git a/migrations/efcore-3.0.sh b/migrations/efcore-3.0.sh
index 420d714972..b2f25f01e6 100644
--- a/migrations/efcore-3.0.sh
+++ b/migrations/efcore-3.0.sh
@@ -1,12 +1,10 @@
#!/usr/bin/env zsh
# Define the modules to update
-mods=("Management")
-# mods=("Alterations" "Runtime" "Management" "Identity" "Labels")
+mods=("Alterations" "Runtime" "Management" "Identity" "Labels")
# Define the list of providers
providers=("MySql" "SqlServer" "Sqlite" "PostgreSql")
-# providers=("SqlServer")
# Connection strings for each provider
typeset -A connStrings
diff --git a/migrations/efcore-3.3.sh b/migrations/efcore-3.3.sh
new file mode 100755
index 0000000000..4f43b0cd88
--- /dev/null
+++ b/migrations/efcore-3.3.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+# Define the modules to update
+mods=("Alterations" "Runtime" "Management" "Identity" "Labels")
+
+# Define the list of providers
+providers=("MySql" "SqlServer" "Sqlite" "PostgreSql")
+
+# Loop through each module
+for module in "${mods[@]}"; do
+ # Loop through each provider
+ for provider in "${providers[@]}"; do
+ providerPath="../src/modules/Elsa.EntityFrameworkCore.$provider"
+ migrationsPath="Migrations/$module"
+
+ echo "Updating migrations for $provider..."
+ echo "Provider path: ${providerPath:?}/${migrationsPath}"
+ echo "Migrations path: $migrationsPath"
+ ef-migration-runtime-schema --interface Elsa.EntityFrameworkCore.Common.Contracts.IElsaDbContextSchema --efOptions "migrations add V3_3 -c ""$module""ElsaDbContext -p ""$providerPath"" -o ""$migrationsPath"""
+ done
+done
diff --git a/samples/aspnet/Elsa.Samples.AspNet.HangfireIntegration/Program.cs b/samples/aspnet/Elsa.Samples.AspNet.HangfireIntegration/Program.cs
index 12dd7010a8..f32d617067 100644
--- a/samples/aspnet/Elsa.Samples.AspNet.HangfireIntegration/Program.cs
+++ b/samples/aspnet/Elsa.Samples.AspNet.HangfireIntegration/Program.cs
@@ -33,7 +33,6 @@
// Configure identity.
elsa.UseIdentity(identity =>
{
- identity.IdentityOptions = options => identitySection.Bind(options);
identity.TokenOptions = options => identityTokenSection.Bind(options);
identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options));
identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options));
diff --git a/samples/aspnet/Elsa.Samples.AspNet.ProtoActorRuntime.AzureContainerApps/Program.cs b/samples/aspnet/Elsa.Samples.AspNet.ProtoActorRuntime.AzureContainerApps/Program.cs
index 5a838ec321..88545bbaa7 100644
--- a/samples/aspnet/Elsa.Samples.AspNet.ProtoActorRuntime.AzureContainerApps/Program.cs
+++ b/samples/aspnet/Elsa.Samples.AspNet.ProtoActorRuntime.AzureContainerApps/Program.cs
@@ -36,7 +36,6 @@
.AddActivitiesFrom()
.UseIdentity(identity =>
{
- identity.IdentityOptions = options => identitySection.Bind(options);
identity.TokenOptions = options => identityTokenSection.Bind(options);
identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options));
identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options));
diff --git a/samples/aspnet/Elsa.Samples.AspNet.ProtoActorRuntime/Program.cs b/samples/aspnet/Elsa.Samples.AspNet.ProtoActorRuntime/Program.cs
index 6c5e720371..29e33eec54 100644
--- a/samples/aspnet/Elsa.Samples.AspNet.ProtoActorRuntime/Program.cs
+++ b/samples/aspnet/Elsa.Samples.AspNet.ProtoActorRuntime/Program.cs
@@ -19,7 +19,6 @@
.AddActivitiesFrom()
.UseIdentity(identity =>
{
- identity.IdentityOptions = options => identitySection.Bind(options);
identity.TokenOptions = options => identityTokenSection.Bind(options);
identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options));
identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options));
diff --git a/samples/aspnet/Elsa.Samples.AspNet.QuartzIntegration/Program.cs b/samples/aspnet/Elsa.Samples.AspNet.QuartzIntegration/Program.cs
index 4f1738b9d5..52dbb5e9ed 100644
--- a/samples/aspnet/Elsa.Samples.AspNet.QuartzIntegration/Program.cs
+++ b/samples/aspnet/Elsa.Samples.AspNet.QuartzIntegration/Program.cs
@@ -28,7 +28,6 @@
// Configure identity.
elsa.UseIdentity(identity =>
{
- identity.IdentityOptions = options => identitySection.Bind(options);
identity.TokenOptions = options => identityTokenSection.Bind(options);
identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options));
identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options));
diff --git a/samples/aspnet/Elsa.Samples.AspNet.TelnyxIntegration/Program.cs b/samples/aspnet/Elsa.Samples.AspNet.TelnyxIntegration/Program.cs
index fab4ec6067..5b3dd0f735 100644
--- a/samples/aspnet/Elsa.Samples.AspNet.TelnyxIntegration/Program.cs
+++ b/samples/aspnet/Elsa.Samples.AspNet.TelnyxIntegration/Program.cs
@@ -15,11 +15,9 @@
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
var configuration = builder.Configuration;
-var identityOptions = new IdentityOptions();
var identityTokenOptions = new IdentityTokenOptions();
var identitySection = configuration.GetSection("Identity");
var identityTokenSection = identitySection.GetSection("Tokens");
-identitySection.Bind(identityOptions);
identityTokenSection.Bind(identityTokenOptions);
// Add Elsa services.
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Controllers/RunWorkflowController.cs b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Controllers/RunWorkflowController.cs
new file mode 100644
index 0000000000..cc8eb52233
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Controllers/RunWorkflowController.cs
@@ -0,0 +1,26 @@
+using Elsa.Http;
+using Elsa.Workflows.Contracts;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Elsa.Samples.AspNet.Tenants;
+
+[ApiController]
+[Route("run-workflow")]
+public class RunWorkflowController : ControllerBase
+{
+ private readonly IWorkflowRunner _workflowRunner;
+
+ public RunWorkflowController(IWorkflowRunner workflowRunner)
+ {
+ _workflowRunner = workflowRunner;
+ }
+
+ [HttpGet]
+ public async Task Post()
+ {
+ await _workflowRunner.RunAsync(new WriteHttpResponse
+ {
+ Content = new("Hello ASP.NET world!")
+ });
+ }
+}
\ No newline at end of file
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Elsa.Samples.AspNet.Tenants.External.csproj b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Elsa.Samples.AspNet.Tenants.External.csproj
new file mode 100644
index 0000000000..2005a848e7
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Elsa.Samples.AspNet.Tenants.External.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net8.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Program.cs b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Program.cs
new file mode 100644
index 0000000000..d05b27b444
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Program.cs
@@ -0,0 +1,154 @@
+using System.Security.Claims;
+using Elsa;
+using Elsa.EntityFrameworkCore.Common;
+using Elsa.EntityFrameworkCore.Extensions;
+using Elsa.EntityFrameworkCore.Modules.Management;
+using Elsa.EntityFrameworkCore.Modules.Runtime;
+using Elsa.Extensions;
+using Elsa.Identity.MultiTenancy;
+using Elsa.Tenants.Extensions;
+using FastEndpoints.Swagger;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using NSwag;
+using NSwag.AspNetCore;
+using NSwag.Generation.Processors.Security;
+
+WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
+ConfigurationManager configuration = builder.Configuration;
+
+var identitySection = configuration.GetSection("Identity");
+var tenantsSection = configuration.GetSection("Multitenancy");
+
+// Add services to the container.
+builder.Services.AddControllers();
+builder.Services.AddElsa(elsa =>
+{
+ var dbContextOptions = new ElsaDbContextOptions();
+ string sqliteConnectionString = configuration.GetConnectionString("Sqlite")!;
+ string schema = configuration.GetConnectionString("Schema")!;
+
+ if (!string.IsNullOrEmpty(schema))
+ {
+ dbContextOptions.SchemaName = schema;
+ dbContextOptions.MigrationsAssemblyName = typeof(Program).Assembly.GetName().Name;
+ }
+
+ elsa.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString, dbContextOptions)));
+ elsa.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString, dbContextOptions)));
+ elsa.UseIdentity(options => identitySection.Bind(options));
+ elsa.UseTenants(tenantsFeature =>
+ {
+ tenantsFeature.TenantsOptions = options =>
+ {
+ tenantsSection.Bind(options);
+ options.TenantResolutionPipelineBuilder.Append();
+ };
+ tenantsFeature.UseConfigurationBasedTenantsProvider();
+ });
+
+ elsa
+ .UseHttp(options =>
+ {
+ options.ConfigureHttpOptions = httpOptions =>
+ {
+ httpOptions.BaseUrl = new Uri("https://localhost:8765");
+ httpOptions.BasePath = "/workflows-http-endpoints";
+ };
+ })
+ .AddFastEndpointsAssembly()
+ .UseWorkflowsApi()
+ .UseScheduling()
+ .UseRealTimeWorkflows()
+ .UseJavaScript()
+ .UseLiquid();
+});
+
+builder.Services
+ .AddAuthentication("Bearer")
+ .AddJwtBearer(options =>
+ {
+ options.Authority = configuration.GetValue("Authentication:Authority");
+ options.Audience = configuration.GetValue("Authentication:Audience");
+ options.RequireHttpsMetadata = false;
+
+ options.Events = new JwtBearerEvents
+ {
+ OnTokenValidated = (context) =>
+ {
+ //Simplification of the Elsa permissions by granting access to everything
+ var identity = context.Principal.Identity as ClaimsIdentity;
+ identity?.AddClaim(new Claim("permissions", PermissionNames.All));
+ return Task.CompletedTask;
+ }
+ };
+ });
+
+builder.Services.SwaggerDocument(options =>
+{
+ options.DocumentSettings = documentSetting =>
+ {
+ documentSetting.Title = "Elsa API";
+ documentSetting.Version = "v1";
+
+ documentSetting.AddSecurity("bearer", Enumerable.Empty(), new OpenApiSecurityScheme
+ {
+ Type = OpenApiSecuritySchemeType.OAuth2,
+ Description = "OIDC",
+ Flow = OpenApiOAuth2Flow.Implicit,
+ Flows = new OpenApiOAuthFlows()
+ {
+ AuthorizationCode = new OpenApiOAuthFlow()
+ {
+ AuthorizationUrl = $"{configuration.GetValue("Authentication:Authority")}/connect/authorize",
+ TokenUrl = $"{configuration.GetValue("Authentication:Authority")}/connect/token",
+ Scopes = new Dictionary
+ {
+ {
+ "workflows.*", "workflows.*"
+ },
+ {
+ "openid", "OpenId"
+ },
+ {
+ "profile", "Profile"
+ },
+ },
+ },
+ }
+ });
+ documentSetting.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("bearer"));
+ };
+});
+
+builder.Services.AddCors(options => options.AddDefaultPolicy(policy => policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()));
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+app.UseHttpsRedirection();
+app.UseRouting();
+app.UseAuthorization();
+app.MapControllers();
+app.UseCors();
+app.UseWorkflows();
+app.UseWorkflowsApi("api");
+app.UseWorkflowsSignalRHubs();
+
+if (!app.Environment.IsProduction())
+{
+ app.UseOpenApi(options => options.PostProcess = (document, _) => document.Servers.Clear());
+ app.UseSwaggerUi(options =>
+ {
+ options.OAuth2Client = new OAuth2ClientSettings
+ {
+ ClientId = configuration.GetValue("Authentication:ClientId"),
+ ClientSecret = configuration.GetValue("Authentication:ClientSecret"),
+ AppName = configuration.GetValue("Authentication:Audience"),
+ UsePkceWithAuthorizationCodeGrant = true,
+ };
+ options.OAuth2Client.Scopes.AddRange("workflows.*", "openid", "profile");
+ });
+ app.UseReDoc();
+}
+
+app.Run();
\ No newline at end of file
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Properties/launchSettings.json b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Properties/launchSettings.json
new file mode 100644
index 0000000000..114397e425
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Properties/launchSettings.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:8764",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:8765;http://localhost:8764",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Workflows/HelloWorldHttpWorkflow.cs b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Workflows/HelloWorldHttpWorkflow.cs
new file mode 100644
index 0000000000..4b736b64df
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/Workflows/HelloWorldHttpWorkflow.cs
@@ -0,0 +1,28 @@
+using Elsa.Http;
+using Elsa.Workflows;
+using Elsa.Workflows.Activities;
+using Elsa.Workflows.Contracts;
+
+namespace Elsa.Samples.AspNet.Tenants;
+
+public class HelloWorldHttpWorkflow : WorkflowBase
+{
+ protected override void Build(IWorkflowBuilder builder)
+ {
+ builder.Root = new Sequence
+ {
+ Activities =
+ {
+ new HttpEndpoint
+ {
+ Path = new("/hello-world"),
+ CanStartWorkflow = true
+ },
+ new WriteHttpResponse
+ {
+ Content = new("Hello world of HTTP workflows!")
+ }
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/appsettings.json b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/appsettings.json
new file mode 100644
index 0000000000..ebbc7a7a71
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants.External/appsettings.json
@@ -0,0 +1,59 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "ConnectionStrings": {
+ "Sqlite": "Data Source=elsa.sqlite.db;Cache=Shared;"
+ },
+ "Multitenancy": {
+ "TenantIdClaimsType": "instance_token",
+ "Tenants": [
+ {
+ "TenantId": "19a5f2b0-75e4-45ad-b0ae-fe02d77867d0",
+ "Name": "DefaultTenant"
+ },
+ {
+ "TenantId": "a8ad404f-2ac1-439f-8856-f225c928d811",
+ "Name": "Tenant1"
+ }
+ ]
+ },
+ "Authentication": {
+ "Authority": "https://authorization-server.com",
+ "Audience": "Elsa",
+ "ClientId": "elsa.client",
+ "ClientSecret": "elsa.client-secret"
+ },
+ "Identity": {
+ "Tokens": {
+ "SigningKey": "secret-signing-key",
+ "AccessTokenLifetime": "1:00:00:00",
+ "RefreshTokenLifetime": "7:00:00:00"
+ },
+ "Roles": [
+ {
+ "Id": "admin",
+ "Name": "Administrator",
+ "Permissions": [
+ "*"
+ ]
+ }
+ ],
+ "Users": [
+ {
+ "Id": "a2323f46-42db-4e15-af8b-94238717d817",
+ "Name": "admin",
+ "TenantId": "19a5f2b0-75e4-45ad-b0ae-fe02d77867d0",
+ "HashedPassword": "TfKzh9RLix6FPcCNeHLkGrysFu3bYxqzGqduNdi8v1U=",
+ "HashedPasswordSalt": "JEy9kBlhHCNsencitRHlGxmErmSgY+FVyMJulCH27Ds=",
+ "Roles": [
+ "admin"
+ ]
+ }
+ ]
+ }
+}
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants/Controllers/RunWorkflowController.cs b/samples/aspnet/Elsa.Samples.AspNet.Tenants/Controllers/RunWorkflowController.cs
new file mode 100644
index 0000000000..cc8eb52233
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants/Controllers/RunWorkflowController.cs
@@ -0,0 +1,26 @@
+using Elsa.Http;
+using Elsa.Workflows.Contracts;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Elsa.Samples.AspNet.Tenants;
+
+[ApiController]
+[Route("run-workflow")]
+public class RunWorkflowController : ControllerBase
+{
+ private readonly IWorkflowRunner _workflowRunner;
+
+ public RunWorkflowController(IWorkflowRunner workflowRunner)
+ {
+ _workflowRunner = workflowRunner;
+ }
+
+ [HttpGet]
+ public async Task Post()
+ {
+ await _workflowRunner.RunAsync(new WriteHttpResponse
+ {
+ Content = new("Hello ASP.NET world!")
+ });
+ }
+}
\ No newline at end of file
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants/Elsa.Samples.AspNet.Tenants.csproj b/samples/aspnet/Elsa.Samples.AspNet.Tenants/Elsa.Samples.AspNet.Tenants.csproj
new file mode 100644
index 0000000000..e3341cc477
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants/Elsa.Samples.AspNet.Tenants.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net8.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants/Program.cs b/samples/aspnet/Elsa.Samples.AspNet.Tenants/Program.cs
new file mode 100644
index 0000000000..82b32b5603
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants/Program.cs
@@ -0,0 +1,90 @@
+using Elsa;
+using Elsa.EntityFrameworkCore.Common;
+using Elsa.EntityFrameworkCore.Extensions;
+using Elsa.EntityFrameworkCore.Modules.Management;
+using Elsa.EntityFrameworkCore.Modules.Runtime;
+using Elsa.Extensions;
+using Elsa.Identity.MultiTenancy;
+using Elsa.Tenants.Extensions;
+using FastEndpoints.Swagger;
+
+WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
+ConfigurationManager configuration = builder.Configuration;
+
+var identitySection = configuration.GetSection("Identity");
+var identityTokenSection = identitySection.GetSection("Tokens");
+var multiTenancySection = configuration.GetSection("Multitenancy");
+
+// Add services to the container.
+builder.Services.AddControllers();
+builder.Services.AddElsa(elsa =>
+{
+ var dbContextOptions = new ElsaDbContextOptions();
+ string sqliteConnectionString = configuration.GetConnectionString("Sqlite")!;
+ string schema = configuration.GetConnectionString("Schema")!;
+
+ if (!string.IsNullOrEmpty(schema))
+ {
+ dbContextOptions.SchemaName = schema;
+ dbContextOptions.MigrationsAssemblyName = typeof(Program).Assembly.GetName().Name;
+ }
+
+ elsa.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString, dbContextOptions)));
+ elsa.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString, dbContextOptions)));
+
+ elsa.UseSasTokens()
+ .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();
+
+ elsa.UseTenants(tenantsFeature =>
+ {
+ tenantsFeature.TenantsOptions = options =>
+ {
+ multiTenancySection.Bind(options);
+ options.TenantResolutionPipelineBuilder.Append();
+ };
+ tenantsFeature.UseConfigurationBasedTenantsProvider();
+ });
+
+ elsa
+ .UseHttp()
+ .AddFastEndpointsAssembly()
+ .UseWorkflowsApi();
+
+ elsa.AddWorkflowsFrom();
+});
+
+builder.Services.SwaggerDocument(options =>
+{
+ options.DocumentSettings = documentSetting =>
+ {
+ documentSetting.Title = "Elsa API";
+ documentSetting.Version = "v1";
+ };
+});
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+app.UseHttpsRedirection();
+app.UseAuthorization();
+app.MapControllers();
+app.UseWorkflows();
+app.UseWorkflowsApi();
+
+if (!app.Environment.IsProduction())
+{
+ EndpointSecurityOptions.SecurityIsEnabled = false;
+
+ app.UseOpenApi();
+ app.UseSwaggerUi();
+ app.UseReDoc();
+}
+
+app.Run();
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants/Properties/launchSettings.json b/samples/aspnet/Elsa.Samples.AspNet.Tenants/Properties/launchSettings.json
new file mode 100644
index 0000000000..01ac0b6cb3
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants/Properties/launchSettings.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5284",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7285;http://localhost:5284",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants/Workflows/HelloWorldHttpWorkflow.cs b/samples/aspnet/Elsa.Samples.AspNet.Tenants/Workflows/HelloWorldHttpWorkflow.cs
new file mode 100644
index 0000000000..4b736b64df
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants/Workflows/HelloWorldHttpWorkflow.cs
@@ -0,0 +1,28 @@
+using Elsa.Http;
+using Elsa.Workflows;
+using Elsa.Workflows.Activities;
+using Elsa.Workflows.Contracts;
+
+namespace Elsa.Samples.AspNet.Tenants;
+
+public class HelloWorldHttpWorkflow : WorkflowBase
+{
+ protected override void Build(IWorkflowBuilder builder)
+ {
+ builder.Root = new Sequence
+ {
+ Activities =
+ {
+ new HttpEndpoint
+ {
+ Path = new("/hello-world"),
+ CanStartWorkflow = true
+ },
+ new WriteHttpResponse
+ {
+ Content = new("Hello world of HTTP workflows!")
+ }
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/samples/aspnet/Elsa.Samples.AspNet.Tenants/appsettings.json b/samples/aspnet/Elsa.Samples.AspNet.Tenants/appsettings.json
new file mode 100644
index 0000000000..bf47eb0c34
--- /dev/null
+++ b/samples/aspnet/Elsa.Samples.AspNet.Tenants/appsettings.json
@@ -0,0 +1,67 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "ConnectionStrings": {
+ "Sqlite": "Data Source=elsa.sqlite.db;Cache=Shared;"
+ },
+ "Identity": {
+ "Tokens": {
+ "SigningKey": "secret-signing-key",
+ "AccessTokenLifetime": "1:00:00:00",
+ "RefreshTokenLifetime": "7:00:00:00"
+ },
+ "Roles": [
+ {
+ "Id": "admin",
+ "Name": "Administrator",
+ "Permissions": [
+ "*"
+ ]
+ }
+ ],
+ "Users": [
+ {
+ "Id": "a2323f46-42db-4e15-af8b-94238717d817",
+ "Name": "admin",
+ "TenantId": "19a5f2b0-75e4-45ad-b0ae-fe02d77867d0",
+ "HashedPassword": "TfKzh9RLix6FPcCNeHLkGrysFu3bYxqzGqduNdi8v1U=",
+ "HashedPasswordSalt": "JEy9kBlhHCNsencitRHlGxmErmSgY+FVyMJulCH27Ds=",
+ "Roles": [
+ "admin"
+ ]
+ }
+ ]
+ },
+ "Multitenancy": {
+ "Tenants": [
+ {
+ "TenantId": "19a5f2b0-75e4-45ad-b0ae-fe02d77867d0",
+ "Name": "DefaultTenant"
+ },
+ {
+ "TenantId": "a8ad404f-2ac1-439f-8856-f225c928d811",
+ "Name": "Tenant1"
+ }
+ ]
+ },
+ "Applications": [
+ {
+ "id": "d57030226341448daff5a2935aba2d3f",
+ "name": "Postman",
+ "roles": [
+ "admin"
+ ],
+ "clientId": "HXr0Vzdm9KCZbwsJ",
+ "clientSecret": "a<~QGGHTEA%u4;CU&'Wga5ED:_&Gd1C)",
+ "hashedApiKey": "Z5ClHs3mbzx8Pnw3+PxbMq8A/Y+VKMCCDTGYtax8JFM=",
+ "hashedApiKeySalt": "kBisa1X8FwBfN2zmyGMFRgIVVBleghhQAJ4WGyTkaD0=",
+ "hashedClientSecret": "jEv58d0SVbGQ3nBZM0lkzHghG4Y+lMKW80wipz+9vHk=",
+ "hashedClientSecretSalt": "xRKy14Ok1/tU3kLf/8V1fcbLIegy9vcM90Peu2tzohU="
+ }
+ ]
+}
\ No newline at end of file
diff --git a/samples/aspnet/Elsa.Samples.AspNet.WorkflowContexts/Program.cs b/samples/aspnet/Elsa.Samples.AspNet.WorkflowContexts/Program.cs
index 3958692647..435ac79617 100644
--- a/samples/aspnet/Elsa.Samples.AspNet.WorkflowContexts/Program.cs
+++ b/samples/aspnet/Elsa.Samples.AspNet.WorkflowContexts/Program.cs
@@ -23,7 +23,6 @@
.AddWorkflow()
.UseIdentity(identity =>
{
- identity.IdentityOptions = options => identitySection.Bind(options);
identity.TokenOptions = options => identityTokenSection.Bind(options);
identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options));
identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options));
diff --git a/samples/console/Elsa.Samples.ConsoleApp.JsonWorkflows/Program.cs b/samples/console/Elsa.Samples.ConsoleApp.JsonWorkflows/Program.cs
index 20b986bd3a..88f328d977 100644
--- a/samples/console/Elsa.Samples.ConsoleApp.JsonWorkflows/Program.cs
+++ b/samples/console/Elsa.Samples.ConsoleApp.JsonWorkflows/Program.cs
@@ -1,7 +1,7 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Elsa.Workflows.Contracts;
-using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management;
using Microsoft.Extensions.DependencyInjection;
// Setup service container.
diff --git a/src/bundles/Elsa.Server.Web/Endpoints/DynamicWorkflows/Post/Endpoint.cs b/src/bundles/Elsa.Server.Web/Endpoints/DynamicWorkflows/Post/Endpoint.cs
index 300f494340..73ca592316 100644
--- a/src/bundles/Elsa.Server.Web/Endpoints/DynamicWorkflows/Post/Endpoint.cs
+++ b/src/bundles/Elsa.Server.Web/Endpoints/DynamicWorkflows/Post/Endpoint.cs
@@ -4,7 +4,6 @@
using Elsa.Workflows.Options;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
-using Elsa.Workflows.Runtime.Options;
using Elsa.Workflows.Runtime.Parameters;
namespace Elsa.Server.Web.Endpoints.DynamicWorkflows.Post;
diff --git a/src/bundles/Elsa.Server.Web/Enums/PersistenceProvider.cs b/src/bundles/Elsa.Server.Web/Enums/PersistenceProvider.cs
new file mode 100644
index 0000000000..31e3e33b8d
--- /dev/null
+++ b/src/bundles/Elsa.Server.Web/Enums/PersistenceProvider.cs
@@ -0,0 +1,9 @@
+namespace Elsa.Server.Web;
+
+public enum PersistenceProvider
+{
+ Memory,
+ EntityFrameworkCore,
+ MongoDb,
+ Dapper
+}
\ No newline at end of file
diff --git a/src/bundles/Elsa.Server.Web/Enums/SqlDatabaseProvider.cs b/src/bundles/Elsa.Server.Web/Enums/SqlDatabaseProvider.cs
new file mode 100644
index 0000000000..cc29880428
--- /dev/null
+++ b/src/bundles/Elsa.Server.Web/Enums/SqlDatabaseProvider.cs
@@ -0,0 +1,10 @@
+namespace Elsa.Server.Web;
+
+public enum SqlDatabaseProvider
+{
+ SqlServer,
+ Sqlite,
+ MySql,
+ PostgreSql,
+ CockroachDb
+}
\ No newline at end of file
diff --git a/src/bundles/Elsa.Server.Web/Program.cs b/src/bundles/Elsa.Server.Web/Program.cs
index d90f910a56..67562657de 100644
--- a/src/bundles/Elsa.Server.Web/Program.cs
+++ b/src/bundles/Elsa.Server.Web/Program.cs
@@ -13,8 +13,10 @@
using Elsa.EntityFrameworkCore.Modules.Runtime;
using Elsa.Extensions;
using Elsa.Features.Services;
+using Elsa.Http.MultiTenancy;
using Elsa.Http.Options;
using Elsa.MassTransit.Extensions;
+using Elsa.Identity.MultiTenancy;
using Elsa.MongoDb.Extensions;
using Elsa.MongoDb.Modules.Alterations;
using Elsa.MongoDb.Modules.Identity;
@@ -22,6 +24,7 @@
using Elsa.MongoDb.Modules.Runtime;
using Elsa.Server.Web;
using Elsa.Workflows;
+using Elsa.Tenants.Extensions;
using Elsa.Workflows.Management.Compression;
using Elsa.Workflows.Management.Stores;
using Elsa.Workflows.Runtime.Distributed.Extensions;
@@ -35,23 +38,21 @@
using Proto.Persistence.Sqlite;
using Proto.Persistence.SqlServer;
-const bool useMongoDb = false;
-const bool useSqlServer = false;
-const bool usePostgres = false;
-const bool useCockroachDb = false;
-const bool useDapper = false;
+const PersistenceProvider persistenceProvider = PersistenceProvider.EntityFrameworkCore;
+const SqlDatabaseProvider sqlDatabaseProvider = SqlDatabaseProvider.Sqlite;
const bool useHangfire = false;
const bool useQuartz = true;
const bool useMassTransit = true;
const bool useZipCompression = false;
const bool runEFCoreMigrations = true;
const bool useMemoryStores = false;
-const bool useCaching = true;
+const bool useCaching = false;
const bool useAzureServiceBusModule = false;
const bool useReadOnlyMode = false;
const WorkflowRuntime workflowRuntime = WorkflowRuntime.ProtoActor;
const DistributedCachingTransport distributedCachingTransport = DistributedCachingTransport.MassTransit;
const MassTransitBroker massTransitBroker = MassTransitBroker.Memory;
+const bool useMultitenancy = false;
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
@@ -73,22 +74,22 @@
services
.AddElsa(elsa =>
{
- if (useMongoDb)
+ if (persistenceProvider == PersistenceProvider.MongoDb)
elsa.UseMongoDb(mongoDbConnectionString);
- if (useDapper)
+ if (persistenceProvider == PersistenceProvider.Dapper)
elsa.UseDapper(dapper =>
{
dapper.UseMigrations(feature =>
{
- if (useSqlServer)
+ if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer)
feature.UseSqlServer();
else
feature.UseSqlite();
});
dapper.DbConnectionProvider = sp =>
{
- if (useSqlServer)
+ if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer)
return new SqlServerDbConnectionProvider(sqlServerConnectionString!);
else
return new SqliteDbConnectionProvider(sqliteConnectionString);
@@ -105,26 +106,25 @@
.UseFileStorage()
.UseIdentity(identity =>
{
- if (useMongoDb)
+ if (persistenceProvider == PersistenceProvider.MongoDb)
identity.UseMongoDb();
- else if (useDapper)
+ else if (persistenceProvider == PersistenceProvider.Dapper)
identity.UseDapper();
else
identity.UseEntityFrameworkCore(ef =>
{
- if (useSqlServer)
+ if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer)
ef.UseSqlServer(sqlServerConnectionString!);
- else if (usePostgres)
+ else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
ef.UsePostgreSql(postgresConnectionString!);
- else if (useCockroachDb)
+ else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
else
ef.UseSqlite(sqliteConnectionString);
ef.RunMigrations = runEFCoreMigrations;
});
-
- identity.IdentityOptions = options => identitySection.Bind(options);
+
identity.TokenOptions = options => identityTokenSection.Bind(options);
identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options));
identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options));
@@ -133,18 +133,18 @@
.UseDefaultAuthentication()
.UseWorkflowManagement(management =>
{
- if (useMongoDb)
+ if (persistenceProvider == PersistenceProvider.MongoDb)
management.UseMongoDb();
- else if (useDapper)
+ else if (persistenceProvider == PersistenceProvider.Dapper)
management.UseDapper();
else
management.UseEntityFrameworkCore(ef =>
{
- if (useSqlServer)
+ if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer)
ef.UseSqlServer(sqlServerConnectionString!);
- else if (usePostgres)
+ else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
ef.UsePostgreSql(postgresConnectionString!);
- else if (useCockroachDb)
+ else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
else
ef.UseSqlite(sqliteConnectionString);
@@ -169,18 +169,18 @@
})
.UseWorkflowRuntime(runtime =>
{
- if (useMongoDb)
+ if (persistenceProvider == PersistenceProvider.MongoDb)
runtime.UseMongoDb();
- else if (useDapper)
+ else if (persistenceProvider == PersistenceProvider.Dapper)
runtime.UseDapper();
else
runtime.UseEntityFrameworkCore(ef =>
{
- if (useSqlServer)
+ if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer)
ef.UseSqlServer(sqlServerConnectionString!);
- else if (usePostgres)
+ else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
ef.UsePostgreSql(postgresConnectionString!);
- else if (useCockroachDb)
+ else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
else
ef.UseSqlite(sqliteConnectionString);
@@ -197,7 +197,7 @@
{
runtime.UseProtoActor(proto => proto.PersistenceProvider = _ =>
{
- if (useSqlServer)
+ if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer)
return new SqlServerProvider(sqlServerConnectionString!, true, "", "proto_actor");
else
return new SqliteProvider(new SqliteConnectionStringBuilder(sqliteConnectionString));
@@ -220,7 +220,7 @@
runtime.UseCache();
runtime.DistributedLockingOptions = options => configuration.GetSection("Runtime:DistributedLocking").Bind(options);
-
+
runtime.DistributedLockProvider = _ =>
{
switch (distributedLockProviderName)
@@ -293,11 +293,11 @@
.UseEmail(email => email.ConfigureOptions = options => configuration.GetSection("Smtp").Bind(options))
.UseAlterations(alterations =>
{
- if (useMongoDb)
+ if (persistenceProvider == PersistenceProvider.MongoDb)
{
alterations.UseMongoDb();
}
- else if (useDapper)
+ else if (persistenceProvider == PersistenceProvider.Dapper)
{
// TODO: alterations.UseDapper();
}
@@ -305,11 +305,11 @@
{
alterations.UseEntityFrameworkCore(ef =>
{
- if (useSqlServer)
+ if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer)
ef.UseSqlServer(sqlServerConnectionString);
- else if (usePostgres)
+ else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
ef.UsePostgreSql(postgresConnectionString);
- else if (useCockroachDb)
+ else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
else
ef.UseSqlite(sqliteConnectionString);
@@ -378,6 +378,20 @@
});
}
+ if (useMultitenancy)
+ elsa.UseTenants(tenants =>
+ {
+ tenants.TenantsOptions = options =>
+ {
+ configuration.GetSection("Multitenancy").Bind(options);
+ options.TenantResolutionPipelineBuilder
+ .Append()
+ .Append()
+ .Append();
+ };
+ tenants.UseConfigurationBasedTenantsProvider();
+ });
+
elsa.InstallDropIns(options => options.DropInRootDirectory = Path.Combine(Directory.GetCurrentDirectory(), "App_Data", "DropIns"));
elsa.AddSwagger();
elsa.AddFastEndpointsAssembly();
@@ -393,11 +407,6 @@
// Build the web application.
var app = builder.Build();
-// app.UseSimulatedLatency(
-// TimeSpan.FromMilliseconds(1000),
-// TimeSpan.FromMilliseconds(3000)
-// );
-
// Configure the pipeline.
if (app.Environment.IsDevelopment())
app.UseDeveloperExceptionPage();
diff --git a/src/bundles/Elsa.Server.Web/appsettings.json b/src/bundles/Elsa.Server.Web/appsettings.json
index f1ffa3c3d7..94d3973f7c 100644
--- a/src/bundles/Elsa.Server.Web/appsettings.json
+++ b/src/bundles/Elsa.Server.Web/appsettings.json
@@ -27,6 +27,27 @@
"RabbitMq": "amqp://guest:guest@localhost:5672",
"Redis": "localhost:6379,abortConnect=false"
},
+ "Multitenancy": {
+ "Tenants": [
+ {
+ "Id": "default",
+ "Name": "Default"
+ },
+ {
+ "Id": "tenant-1",
+ "Name": "Tenant 1"
+ },
+ {
+ "Id": "tenant-2",
+ "Name": "Tenant 2"
+ },
+ {
+ "Id": "tenant-2a",
+ "TenantId": "tenant-2",
+ "Name": "Tenant 2a"
+ }
+ ]
+ },
"Smtp": {
"Host": "localhost",
"Port": 2525,
@@ -55,7 +76,8 @@
"Name": "Administrator",
"Permissions": [
"*"
- ]
+ ],
+ "TenantId": "default"
}
],
"Users": [
@@ -66,22 +88,24 @@
"HashedPasswordSalt": "JEy9kBlhHCNsencitRHlGxmErmSgY+FVyMJulCH27Ds=",
"Roles": [
"admin"
- ]
+ ],
+ "TenantId": "default"
}
],
"Applications": [
{
- "id": "d57030226341448daff5a2935aba2d3f",
- "name": "Postman",
- "roles": [
+ "Id": "d57030226341448daff5a2935aba2d3f",
+ "Name": "Postman",
+ "Roles": [
"admin"
],
- "clientId": "HXr0Vzdm9KCZbwsJ",
- "clientSecret": "a<~QGGHTEA%u4;CU&'Wga5ED:_&Gd1C)",
- "hashedApiKey": "Z5ClHs3mbzx8Pnw3+PxbMq8A/Y+VKMCCDTGYtax8JFM=",
- "hashedApiKeySalt": "kBisa1X8FwBfN2zmyGMFRgIVVBleghhQAJ4WGyTkaD0=",
- "hashedClientSecret": "jEv58d0SVbGQ3nBZM0lkzHghG4Y+lMKW80wipz+9vHk=",
- "hashedClientSecretSalt": "xRKy14Ok1/tU3kLf/8V1fcbLIegy9vcM90Peu2tzohU="
+ "ClientId": "HXr0Vzdm9KCZbwsJ",
+ "ClientSecret": "a<~QGGHTEA%u4;CU&'Wga5ED:_&Gd1C)",
+ "HashedApiKey": "Z5ClHs3mbzx8Pnw3+PxbMq8A/Y+VKMCCDTGYtax8JFM=",
+ "HashedApiKeySalt": "kBisa1X8FwBfN2zmyGMFRgIVVBleghhQAJ4WGyTkaD0=",
+ "HashedClientSecret": "jEv58d0SVbGQ3nBZM0lkzHghG4Y+lMKW80wipz+9vHk=",
+ "HashedClientSecretSalt": "xRKy14Ok1/tU3kLf/8V1fcbLIegy9vcM90Peu2tzohU=",
+ "TenantId": "default"
}
]
},
diff --git a/src/bundles/Elsa.ServerAndStudio.Web/Program.cs b/src/bundles/Elsa.ServerAndStudio.Web/Program.cs
index 608947b9a8..adf2b52ed2 100644
--- a/src/bundles/Elsa.ServerAndStudio.Web/Program.cs
+++ b/src/bundles/Elsa.ServerAndStudio.Web/Program.cs
@@ -42,7 +42,6 @@
.UseSasTokens()
.UseIdentity(identity =>
{
- identity.IdentityOptions = options => identitySection.Bind(options);
identity.TokenOptions = options => identityTokenSection.Bind(options);
identity.UseConfigurationBasedUserProvider(options => identitySection.Bind(options));
identity.UseConfigurationBasedApplicationProvider(options => identitySection.Bind(options));
diff --git a/src/common/Elsa.Mediator/HostedServices/BackgroundEventPublisherHostedService.cs b/src/common/Elsa.Mediator/HostedServices/BackgroundEventPublisherHostedService.cs
index 10d509f0cc..b467581f91 100644
--- a/src/common/Elsa.Mediator/HostedServices/BackgroundEventPublisherHostedService.cs
+++ b/src/common/Elsa.Mediator/HostedServices/BackgroundEventPublisherHostedService.cs
@@ -65,10 +65,14 @@ await foreach (var notification in output.Reader.ReadAllAsync(cancellationToken)
{
await notificationSender.SendAsync(notification, NotificationStrategy.Sequential, cancellationToken);
}
+ catch (OperationCanceledException e)
+ {
+ _logger.LogDebug(e, "An operation was cancelled while processing the queue");
+ }
catch (Exception e)
{
_logger.LogError(e, "An unhandled exception occured while processing the queue");
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/common/Elsa.Mediator/Middleware/Notification/Components/NotificationHandlerInvokerMiddleware.cs b/src/common/Elsa.Mediator/Middleware/Notification/Components/NotificationHandlerInvokerMiddleware.cs
index 95e3d54ae6..c555a7f5f1 100644
--- a/src/common/Elsa.Mediator/Middleware/Notification/Components/NotificationHandlerInvokerMiddleware.cs
+++ b/src/common/Elsa.Mediator/Middleware/Notification/Components/NotificationHandlerInvokerMiddleware.cs
@@ -43,4 +43,4 @@ public async ValueTask InvokeAsync(NotificationContext context)
// Invoke next middleware.
await _next(context);
}
-}
\ No newline at end of file
+}
diff --git a/src/common/Elsa.Testing.Shared.Component/Elsa.Testing.Shared.Component.csproj b/src/common/Elsa.Testing.Shared.Component/Elsa.Testing.Shared.Component.csproj
index 2a79c24468..1989e696e9 100644
--- a/src/common/Elsa.Testing.Shared.Component/Elsa.Testing.Shared.Component.csproj
+++ b/src/common/Elsa.Testing.Shared.Component/Elsa.Testing.Shared.Component.csproj
@@ -20,12 +20,9 @@
+
-
-
-
-
diff --git a/src/common/Elsa.Testing.Shared.Component/Services/TestTenantResolutionStrategy.cs b/src/common/Elsa.Testing.Shared.Component/Services/TestTenantResolutionStrategy.cs
new file mode 100644
index 0000000000..5f9a88c897
--- /dev/null
+++ b/src/common/Elsa.Testing.Shared.Component/Services/TestTenantResolutionStrategy.cs
@@ -0,0 +1,13 @@
+using Elsa.Common.Abstractions;
+using Elsa.Common.Contexts;
+using Elsa.Common.Results;
+
+namespace Elsa.Workflows.ComponentTests.Helpers.Services;
+
+public class TestTenantResolutionStrategy : TenantResolutionStrategyBase
+{
+ protected override TenantResolutionResult Resolve(TenantResolutionContext context)
+ {
+ return AutoResolve("Tenant1");
+ }
+}
\ No newline at end of file
diff --git a/src/common/Elsa.Testing.Shared.Component/Services/TestTenantsProvider.cs b/src/common/Elsa.Testing.Shared.Component/Services/TestTenantsProvider.cs
new file mode 100644
index 0000000000..64b3c256cd
--- /dev/null
+++ b/src/common/Elsa.Testing.Shared.Component/Services/TestTenantsProvider.cs
@@ -0,0 +1,20 @@
+using Elsa.Common.Entities;
+using Elsa.Tenants;
+using Elsa.Tenants.Models;
+
+namespace Elsa.Testing.Shared.Services;
+
+public class TestTenantsProvider(params string[] tenantIds) : ITenantsProvider
+{
+ public ValueTask> ListAsync(CancellationToken cancellationToken = default)
+ {
+ var tenants = tenantIds.Select(id => new Tenant {Id = id, Name = id});
+ return new(tenants);
+ }
+
+ public async ValueTask FindAsync(TenantFilter filter, CancellationToken cancellationToken = default)
+ {
+ var query = (await ListAsync(cancellationToken)).AsQueryable();
+ return filter.Apply(query).FirstOrDefault();
+ }
+}
\ No newline at end of file
diff --git a/src/common/Elsa.Testing.Shared.Integration/ServiceProviderExtensions.cs b/src/common/Elsa.Testing.Shared.Integration/ServiceProviderExtensions.cs
index 33ad3a32ed..9b596906e3 100644
--- a/src/common/Elsa.Testing.Shared.Integration/ServiceProviderExtensions.cs
+++ b/src/common/Elsa.Testing.Shared.Integration/ServiceProviderExtensions.cs
@@ -1,7 +1,7 @@
using Elsa.Common.Models;
using Elsa.Workflows;
using Elsa.Workflows.Contracts;
-using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Entities;
using Elsa.Workflows.Management.Models;
using Elsa.Workflows.Models;
@@ -94,7 +94,7 @@ public static async Task ImportWorkflowDefinitionAsync(this
{
var runRequest = new RunWorkflowInstanceRequest
{
- BookmarkId = bookmark.BookmarkId
+ BookmarkId = bookmark.Id
};
response = await workflowClient.RunInstanceAsync(runRequest);
}
diff --git a/src/modules/Elsa.Alterations.Core/Services/WorkflowInstanceFinder.cs b/src/modules/Elsa.Alterations.Core/Services/WorkflowInstanceFinder.cs
index 1c3c3245a2..96bbc9c85b 100644
--- a/src/modules/Elsa.Alterations.Core/Services/WorkflowInstanceFinder.cs
+++ b/src/modules/Elsa.Alterations.Core/Services/WorkflowInstanceFinder.cs
@@ -1,7 +1,7 @@
using Elsa.Alterations.Core.Contracts;
using Elsa.Alterations.Core.Models;
using Elsa.Workflows;
-using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Filters;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
diff --git a/src/modules/Elsa.Alterations/AlterationHandlers/MigrateHandler.cs b/src/modules/Elsa.Alterations/AlterationHandlers/MigrateHandler.cs
index f5a9a0c158..25418edeee 100644
--- a/src/modules/Elsa.Alterations/AlterationHandlers/MigrateHandler.cs
+++ b/src/modules/Elsa.Alterations/AlterationHandlers/MigrateHandler.cs
@@ -2,7 +2,7 @@
using Elsa.Alterations.Core.Abstractions;
using Elsa.Alterations.Core.Contexts;
using Elsa.Common.Models;
-using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
diff --git a/src/modules/Elsa.Alterations/Endpoints/Workflows/Retry/Endpoint.cs b/src/modules/Elsa.Alterations/Endpoints/Workflows/Retry/Endpoint.cs
index 7d81f6084e..c690f65ab2 100644
--- a/src/modules/Elsa.Alterations/Endpoints/Workflows/Retry/Endpoint.cs
+++ b/src/modules/Elsa.Alterations/Endpoints/Workflows/Retry/Endpoint.cs
@@ -2,7 +2,7 @@
using Elsa.Alterations.AlterationTypes;
using Elsa.Alterations.Core.Contracts;
using Elsa.Alterations.Core.Results;
-using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Entities;
using Elsa.Workflows.Management.Filters;
using Elsa.Workflows.Runtime;
diff --git a/src/modules/Elsa.Alterations/Services/DefaultAlterationPlanScheduler.cs b/src/modules/Elsa.Alterations/Services/DefaultAlterationPlanScheduler.cs
index bd36698556..db753bdea7 100644
--- a/src/modules/Elsa.Alterations/Services/DefaultAlterationPlanScheduler.cs
+++ b/src/modules/Elsa.Alterations/Services/DefaultAlterationPlanScheduler.cs
@@ -4,7 +4,7 @@
using Elsa.Common.Contracts;
using Elsa.Common.Models;
using Elsa.Workflows.Contracts;
-using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Requests;
diff --git a/src/modules/Elsa.Alterations/Services/DefaultAlterationRunner.cs b/src/modules/Elsa.Alterations/Services/DefaultAlterationRunner.cs
index d9f5105b05..cc52c3404b 100644
--- a/src/modules/Elsa.Alterations/Services/DefaultAlterationRunner.cs
+++ b/src/modules/Elsa.Alterations/Services/DefaultAlterationRunner.cs
@@ -5,7 +5,7 @@
using Elsa.Common.Contracts;
using Elsa.Workflows;
using Elsa.Workflows.Contracts;
-using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management;
using Elsa.Workflows.Pipelines.WorkflowExecution;
using Elsa.Workflows.Runtime;
using Microsoft.Extensions.Logging;
diff --git a/src/modules/Elsa.AzureServiceBus/Features/AzureServiceBusFeature.cs b/src/modules/Elsa.AzureServiceBus/Features/AzureServiceBusFeature.cs
index 03c1ec0bba..ed4dbf8be6 100644
--- a/src/modules/Elsa.AzureServiceBus/Features/AzureServiceBusFeature.cs
+++ b/src/modules/Elsa.AzureServiceBus/Features/AzureServiceBusFeature.cs
@@ -26,7 +26,7 @@ public AzureServiceBusFeature(IModule module) : base(module)
}
///
- /// A value controlling whether or not queues, topics and subscriptions should be created automatically.
+ /// A value controlling whether queues, topics and subscriptions should be created automatically.
///
public bool CreateQueuesTopicsAndSubscriptions { get; set; } = true;
diff --git a/src/modules/Elsa.Common/Abstractions/TenantResolutionStrategyBase.cs b/src/modules/Elsa.Common/Abstractions/TenantResolutionStrategyBase.cs
new file mode 100644
index 0000000000..4648789a9f
--- /dev/null
+++ b/src/modules/Elsa.Common/Abstractions/TenantResolutionStrategyBase.cs
@@ -0,0 +1,48 @@
+using Elsa.Common.Contexts;
+using Elsa.Common.Contracts;
+using Elsa.Common.Results;
+
+namespace Elsa.Common.Abstractions;
+
+///
+/// Base class for implementing a tenant resolution strategy.
+///
+public abstract class TenantResolutionStrategyBase : ITenantResolutionStrategy
+{
+ ValueTask ITenantResolutionStrategy.ResolveAsync(TenantResolutionContext context)
+ {
+ return ResolveAsync(context);
+ }
+
+ ///
+ /// Implement this method to resolve the tenant.
+ ///
+ protected virtual ValueTask ResolveAsync(TenantResolutionContext context)
+ {
+ return new(Resolve(context));
+ }
+
+ ///
+ /// Implement this method to resolve the tenant.
+ ///
+ ///
+ protected virtual TenantResolutionResult Resolve(TenantResolutionContext context)
+ {
+ return Unresolved();
+ }
+
+ ///
+ /// Creates a new instance of representing a resolved tenant.
+ ///
+ protected TenantResolutionResult Resolved(string tenantId) => new(tenantId);
+
+ ///
+ /// Creates a new instance of representing an unresolved tenant.
+ ///
+ protected TenantResolutionResult Unresolved() => new(null);
+
+ ///
+ /// Automatically resolves the tenant if the tenant ID is not null.
+ ///
+ protected TenantResolutionResult AutoResolve(string? tenantId) => tenantId == null ? Unresolved() : Resolved(tenantId);
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Common/Contexts/TenantResolutionContext.cs b/src/modules/Elsa.Common/Contexts/TenantResolutionContext.cs
new file mode 100644
index 0000000000..8dc9ec2c34
--- /dev/null
+++ b/src/modules/Elsa.Common/Contexts/TenantResolutionContext.cs
@@ -0,0 +1,49 @@
+using Elsa.Common.Entities;
+
+namespace Elsa.Common.Contexts;
+
+///
+/// Represents the context for resolving a tenant in a tenant resolution strategy pipeline.
+///
+///
+/// This class provides the necessary information for resolving a tenant, including a cancellation token
+/// to allow for cancelling the resolution process.
+///
+public class TenantResolutionContext
+{
+ private readonly IDictionary _tenantsDictionary;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TenantResolutionContext(IDictionary tenants, CancellationToken cancellationToken)
+ {
+ CancellationToken = cancellationToken;
+ _tenantsDictionary = tenants;
+ }
+
+ ///
+ /// Gets the cancellation token.
+ ///
+ public CancellationToken CancellationToken { get; } = default;
+
+ ///
+ /// Finds a tenant based on the provided tenant ID.
+ ///
+ /// The tenant ID.
+ /// The found tenant or null if no tenant with the provided ID exists.
+ public Tenant? FindTenant(string tenantId)
+ {
+ return _tenantsDictionary.TryGetValue(tenantId, out var tenant) ? tenant : null;
+ }
+
+ ///
+ /// Finds a tenant based on the provided predicate.
+ ///
+ /// The predicate used to filter the tenants.
+ /// The found tenant or null if no tenant satisfies the predicate.
+ public Tenant? FindTenant(Func predicate)
+ {
+ return _tenantsDictionary.Values.FirstOrDefault(predicate);
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Common/Contracts/ITenantResolutionStrategy.cs b/src/modules/Elsa.Common/Contracts/ITenantResolutionStrategy.cs
new file mode 100644
index 0000000000..7ba3bb5c82
--- /dev/null
+++ b/src/modules/Elsa.Common/Contracts/ITenantResolutionStrategy.cs
@@ -0,0 +1,15 @@
+using Elsa.Common.Contexts;
+using Elsa.Common.Results;
+
+namespace Elsa.Common.Contracts;
+
+///
+/// A tenant resolver strategy in a pipeline of tenant resolvers.
+///
+public interface ITenantResolutionStrategy
+{
+ ///
+ /// Resolves the tenant.
+ ///
+ ValueTask ResolveAsync(TenantResolutionContext context);
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Common/Contracts/ITenantResolver.cs b/src/modules/Elsa.Common/Contracts/ITenantResolver.cs
new file mode 100644
index 0000000000..fe7da6f58e
--- /dev/null
+++ b/src/modules/Elsa.Common/Contracts/ITenantResolver.cs
@@ -0,0 +1,15 @@
+using Elsa.Common.Entities;
+
+namespace Elsa.Common.Contracts;
+
+///
+/// Provides access to the current tenant ID.
+///
+public interface ITenantResolver
+{
+ ///
+ /// Get the current .
+ ///
+ /// Current tenant or null.
+ Task GetTenantAsync(CancellationToken cancellationToken = default);
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Common/Entities/Entity.cs b/src/modules/Elsa.Common/Entities/Entity.cs
index 550be9d65f..2a3d2cd6e9 100644
--- a/src/modules/Elsa.Common/Entities/Entity.cs
+++ b/src/modules/Elsa.Common/Entities/Entity.cs
@@ -9,4 +9,9 @@ public abstract class Entity
/// Gets or sets the ID of this entity.
///
public string Id { get; set; } = default!;
+
+ ///
+ /// Gets or sets the ID of the tenant that own this entity.
+ ///
+ public string? TenantId { get; set; }
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Common/Entities/Tenant.cs b/src/modules/Elsa.Common/Entities/Tenant.cs
new file mode 100644
index 0000000000..315b14282a
--- /dev/null
+++ b/src/modules/Elsa.Common/Entities/Tenant.cs
@@ -0,0 +1,15 @@
+using JetBrains.Annotations;
+
+namespace Elsa.Common.Entities;
+
+///
+/// Represents a tenant.
+///
+[UsedImplicitly]
+public class Tenant : Entity
+{
+ ///
+ /// Gets or sets the name.
+ ///
+ public string Name { get; set; } = default!;
+}
diff --git a/src/modules/Elsa.Common/Features/SystemClockFeature.cs b/src/modules/Elsa.Common/Features/SystemClockFeature.cs
index 027319e11a..76ebc55f3f 100644
--- a/src/modules/Elsa.Common/Features/SystemClockFeature.cs
+++ b/src/modules/Elsa.Common/Features/SystemClockFeature.cs
@@ -19,6 +19,6 @@ public SystemClockFeature(IModule module) : base(module)
///
public override void Apply()
{
- Services.AddScoped();
+ Services.AddSingleton();
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Common/Features/TenantResolverFeature.cs b/src/modules/Elsa.Common/Features/TenantResolverFeature.cs
new file mode 100644
index 0000000000..cb5e869931
--- /dev/null
+++ b/src/modules/Elsa.Common/Features/TenantResolverFeature.cs
@@ -0,0 +1,18 @@
+using Elsa.Common.Contracts;
+using Elsa.Common.Services;
+using Elsa.Features.Abstractions;
+using Elsa.Features.Services;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Elsa.Common.Features;
+
+public class TenantResolverFeature(IModule module) : FeatureBase(module)
+{
+ public Func TenantResolver { get; set; } = sp => sp.GetRequiredService();
+
+ public override void Apply()
+ {
+ Services.AddTransient();
+ Services.AddScoped(TenantResolver);
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Common/Results/TenantResolutionResult.cs b/src/modules/Elsa.Common/Results/TenantResolutionResult.cs
new file mode 100644
index 0000000000..68bfa7188d
--- /dev/null
+++ b/src/modules/Elsa.Common/Results/TenantResolutionResult.cs
@@ -0,0 +1,26 @@
+namespace Elsa.Common.Results;
+
+///
+/// Represents the result of a tenant resolution.
+///
+/// The resolved tenant.
+public record TenantResolutionResult(string? TenantId)
+{
+ ///
+ /// Creates a new instance of representing a resolved tenant.
+ ///
+ /// The resolved tenant.
+ /// A new instance of representing a resolved tenant.
+ public static TenantResolutionResult Resolved(string tenantId) => new(tenantId);
+
+ ///
+ /// Creates a new instance of representing an unresolved tenant.
+ ///
+ /// A new instance of representing an unresolved tenant.
+ public static TenantResolutionResult Unresolved() => new(default(string?));
+
+ ///
+ /// Gets a value indicating whether the tenant has been resolved.
+ ///
+ public bool IsResolved => TenantId != null;
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Common/Services/DefaultTenantResolver.cs b/src/modules/Elsa.Common/Services/DefaultTenantResolver.cs
new file mode 100644
index 0000000000..eb99456cc5
--- /dev/null
+++ b/src/modules/Elsa.Common/Services/DefaultTenantResolver.cs
@@ -0,0 +1,21 @@
+using Elsa.Common.Contracts;
+using Elsa.Common.Entities;
+
+namespace Elsa.Common.Services;
+
+///
+/// An implementation of that always returns the default tenant.
+///
+public class DefaultTenantResolver : ITenantResolver
+{
+ private readonly Tenant _defaultTenant = new()
+ {
+ Id = null!,
+ Name = "Default"
+ };
+
+ public Task GetTenantAsync(CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(_defaultTenant);
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper.Migrations/Identity/Initial.cs b/src/modules/Elsa.Dapper.Migrations/Identity/Initial.cs
index a01fd1cc49..aea88eae57 100644
--- a/src/modules/Elsa.Dapper.Migrations/Identity/Initial.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Identity/Initial.cs
@@ -5,7 +5,7 @@
namespace Elsa.Dapper.Migrations.Identity;
///
-[Migration(30001, "Elsa:Identity:Initial")]
+[Migration(30001, "Elsa:Identity:V3.0")]
[PublicAPI]
public class Initial : Migration
{
diff --git a/src/modules/Elsa.Dapper.Migrations/Identity/V3_1.cs b/src/modules/Elsa.Dapper.Migrations/Identity/V3_1.cs
new file mode 100644
index 0000000000..75d8ab191f
--- /dev/null
+++ b/src/modules/Elsa.Dapper.Migrations/Identity/V3_1.cs
@@ -0,0 +1,24 @@
+using System.Diagnostics.CodeAnalysis;
+using FluentMigrator;
+using JetBrains.Annotations;
+
+namespace Elsa.Dapper.Migrations.Identity;
+
+///
+[Migration(30002, "Elsa:Identity:V3.1")]
+[PublicAPI]
+[SuppressMessage("ReSharper", "InconsistentNaming")]
+public class V3_1 : Migration
+{
+ ///
+ public override void Up()
+ {
+ // No changes
+ }
+
+ ///
+ public override void Down()
+ {
+ // No changes
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper.Migrations/Identity/V3_2.cs b/src/modules/Elsa.Dapper.Migrations/Identity/V3_2.cs
new file mode 100644
index 0000000000..45488fd581
--- /dev/null
+++ b/src/modules/Elsa.Dapper.Migrations/Identity/V3_2.cs
@@ -0,0 +1,28 @@
+using System.Diagnostics.CodeAnalysis;
+using FluentMigrator;
+using JetBrains.Annotations;
+
+namespace Elsa.Dapper.Migrations.Identity;
+
+///
+[Migration(30003, "Elsa:Identity:V3.2")]
+[PublicAPI]
+[SuppressMessage("ReSharper", "InconsistentNaming")]
+public class V3_2 : Migration
+{
+ ///
+ public override void Up()
+ {
+ Alter.Table("Users").AddColumn("TenantId").AsString().Nullable();
+ Alter.Table("Roles").AddColumn("TenantId").AsString().Nullable();
+ Alter.Table("Applications").AddColumn("TenantId").AsString().Nullable();
+ }
+
+ ///
+ public override void Down()
+ {
+ Delete.Column("TenantId").FromTable("Users");
+ Delete.Column("TenantId").FromTable("Roles");
+ Delete.Column("TenantId").FromTable("Applications");
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper.Migrations/Management/Initial.cs b/src/modules/Elsa.Dapper.Migrations/Management/Initial.cs
index b7aa69db99..4fbad50447 100644
--- a/src/modules/Elsa.Dapper.Migrations/Management/Initial.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Management/Initial.cs
@@ -5,7 +5,7 @@
namespace Elsa.Dapper.Migrations.Management;
///
-[Migration(10001, "Elsa:Management:Initial")]
+[Migration(10001, "Elsa:Management:V3.0")]
[PublicAPI]
public class Initial : Migration
{
diff --git a/src/modules/Elsa.Dapper.Migrations/Management/V3_1.cs b/src/modules/Elsa.Dapper.Migrations/Management/V3_1.cs
index 3c54331d1d..bac4582666 100644
--- a/src/modules/Elsa.Dapper.Migrations/Management/V3_1.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Management/V3_1.cs
@@ -5,7 +5,7 @@
namespace Elsa.Dapper.Migrations.Management;
///
-[Migration(10002, "Elsa:Runtime:V3.1")]
+[Migration(10002, "Elsa:Management:V3.1")]
[PublicAPI]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class V3_1 : Migration
@@ -25,7 +25,7 @@ public override void Up()
///
public override void Down()
{
- Delete.Column("IsSystem").FromTable("WorkflowDefinitions");
Delete.Column("IsSystem").FromTable("WorkflowInstances");
+ Delete.Column("IsSystem").FromTable("WorkflowDefinitions");
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper.Migrations/Management/V3_2.cs b/src/modules/Elsa.Dapper.Migrations/Management/V3_2.cs
new file mode 100644
index 0000000000..248e5c5cb9
--- /dev/null
+++ b/src/modules/Elsa.Dapper.Migrations/Management/V3_2.cs
@@ -0,0 +1,26 @@
+using System.Diagnostics.CodeAnalysis;
+using FluentMigrator;
+using JetBrains.Annotations;
+
+namespace Elsa.Dapper.Migrations.Management;
+
+///
+[Migration(10003, "Elsa:Management:V3.2")]
+[PublicAPI]
+[SuppressMessage("ReSharper", "InconsistentNaming")]
+public class V3_2 : Migration
+{
+ ///
+ public override void Up()
+ {
+ Alter.Table("WorkflowDefinitions").AddColumn("TenantId").AsString().Nullable();
+ Alter.Table("WorkflowInstances").AddColumn("TenantId").AsString().Nullable();
+ }
+
+ ///
+ public override void Down()
+ {
+ Delete.Column("TenantId").FromTable("WorkflowDefinitions");
+ Delete.Column("TenantId").FromTable("WorkflowInstances");
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs b/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs
index 221838fbd6..866b4f6d79 100644
--- a/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs
@@ -5,7 +5,7 @@
namespace Elsa.Dapper.Migrations.Runtime;
///
-[Migration(20001, "Elsa:Runtime:Initial")]
+[Migration(20001, "Elsa:Runtime:V3.0")]
[PublicAPI]
public class Initial : Migration
{
diff --git a/src/modules/Elsa.Dapper.Migrations/Runtime/V3_1.cs b/src/modules/Elsa.Dapper.Migrations/Runtime/V3_1.cs
index c438cca442..ff74e9fb24 100644
--- a/src/modules/Elsa.Dapper.Migrations/Runtime/V3_1.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Runtime/V3_1.cs
@@ -16,7 +16,8 @@ public override void Up()
{
Create
.Table("KeyValuePairs")
- .WithColumn("Key").AsString().PrimaryKey()
+ .WithColumn("Id").AsString().PrimaryKey()
+ .WithColumn("Key").AsString().Indexed()
.WithColumn("Value").AsString(MaxValue).NotNullable();
}
diff --git a/src/modules/Elsa.Dapper.Migrations/Runtime/V3_2.cs b/src/modules/Elsa.Dapper.Migrations/Runtime/V3_2.cs
index 02a6cdfe80..c4299bfd4b 100644
--- a/src/modules/Elsa.Dapper.Migrations/Runtime/V3_2.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Runtime/V3_2.cs
@@ -6,7 +6,7 @@
namespace Elsa.Dapper.Migrations.Runtime;
///
-[Migration(20002, "Elsa:Runtime:V3.2")]
+[Migration(20003, "Elsa:Runtime:V3.2")]
[PublicAPI]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class V3_2 : Migration
@@ -15,11 +15,22 @@ public class V3_2 : Migration
public override void Up()
{
Delete.Table("WorkflowInboxMessages");
+ Alter.Table("Triggers").AddColumn("TenantId").AsString().Nullable();
+ Alter.Table("Bookmarks").AddColumn("TenantId").AsString().Nullable();
+ Alter.Table("WorkflowExecutionLogRecords").AddColumn("TenantId").AsString().Nullable();
+ Alter.Table("ActivityExecutionRecords").AddColumn("TenantId").AsString().Nullable();
+ Alter.Table("KeyValuePairs").AddColumn("TenantId").AsString().Nullable();
}
///
public override void Down()
{
+ Delete.Column("TenantId").FromTable("Triggers");
+ Delete.Column("TenantId").FromTable("Bookmarks");
+ Delete.Column("TenantId").FromTable("WorkflowExecutionLogRecords");
+ Delete.Column("TenantId").FromTable("ActivityExecutionRecords");
+ Delete.Column("TenantId").FromTable("KeyValuePairs");
+
IfDatabase("SqlServer", "Oracle", "MySql", "Postgres")
.Create
.Table("WorkflowInboxMessages")
diff --git a/src/modules/Elsa.Dapper/Elsa.Dapper.csproj b/src/modules/Elsa.Dapper/Elsa.Dapper.csproj
index 5015d637d2..e28b191cd9 100644
--- a/src/modules/Elsa.Dapper/Elsa.Dapper.csproj
+++ b/src/modules/Elsa.Dapper/Elsa.Dapper.csproj
@@ -10,6 +10,7 @@
+
diff --git a/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs b/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs
index b0195cf556..a6502e45c2 100644
--- a/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs
+++ b/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs
@@ -119,6 +119,7 @@ public static ParameterizedQuery Count(this ParameterizedQuery query, string exp
public static ParameterizedQuery Is(this ParameterizedQuery query, string field, object? value)
{
if (value == null) return query;
+ if (value is DBNull) return IsNull(query, field);
query.Sql.AppendLine(query.Dialect.And(field));
query.Parameters.Add($"@{field}", value);
@@ -134,6 +135,8 @@ public static ParameterizedQuery Is(this ParameterizedQuery query, string field,
public static ParameterizedQuery IsNot(this ParameterizedQuery query, string field, object? value)
{
if (value == null) return query;
+ if (value is DBNull) return IsNotNull(query, field);
+
query.Sql.AppendLine(query.Dialect.AndNot(field));
query.Parameters.Add($"@{field}", value);
diff --git a/src/modules/Elsa.Dapper/Extensions/ServiceCollectionExtensions.cs b/src/modules/Elsa.Dapper/Extensions/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..42c11854e3
--- /dev/null
+++ b/src/modules/Elsa.Dapper/Extensions/ServiceCollectionExtensions.cs
@@ -0,0 +1,22 @@
+using Elsa.Common.Entities;
+using Elsa.Dapper.Records;
+using Elsa.Dapper.Services;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Elsa.Dapper.Extensions;
+
+///
+/// Extension methods for to register Dapper stores.
+///
+public static class ServiceCollectionExtensions
+{
+ ///
+ /// Registers a Dapper store.
+ ///
+ public static IServiceCollection AddDapperStore(this IServiceCollection services, string tableName, string primaryKey = nameof(Entity.Id)) where TStore : class where TRecord : Record
+ {
+ services.AddScoped();
+ services.AddScoped(sp => ActivatorUtilities.CreateInstance>(sp, tableName, primaryKey));
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Identity/Features/Feature.cs b/src/modules/Elsa.Dapper/Modules/Identity/Features/Feature.cs
index 76264fcc7a..6e6d628748 100644
--- a/src/modules/Elsa.Dapper/Modules/Identity/Features/Feature.cs
+++ b/src/modules/Elsa.Dapper/Modules/Identity/Features/Feature.cs
@@ -1,4 +1,6 @@
+using Elsa.Dapper.Extensions;
using Elsa.Dapper.Features;
+using Elsa.Dapper.Modules.Identity.Records;
using Elsa.Dapper.Modules.Identity.Stores;
using Elsa.Features.Abstractions;
using Elsa.Features.Attributes;
@@ -38,8 +40,8 @@ public override void Apply()
{
base.Apply();
- Services.AddScoped();
- Services.AddScoped();
- Services.AddScoped();
+ Services.AddDapperStore("Users");
+ Services.AddDapperStore("Applications");
+ Services.AddDapperStore("Roles");
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Identity/Records/ApplicationRecord.cs b/src/modules/Elsa.Dapper/Modules/Identity/Records/ApplicationRecord.cs
index 3c83131a4b..2a60ce2dc3 100644
--- a/src/modules/Elsa.Dapper/Modules/Identity/Records/ApplicationRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Identity/Records/ApplicationRecord.cs
@@ -1,8 +1,9 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Identity.Records;
-internal class ApplicationRecord
+internal class ApplicationRecord : Record
{
- public string Id { get; set; } = default!;
public string ClientId { get; set; } = default!;
public string HashedClientSecret { get; set; } = default!;
public string HashedClientSecretSalt { get; set; } = default!;
diff --git a/src/modules/Elsa.Dapper/Modules/Identity/Records/RoleRecord.cs b/src/modules/Elsa.Dapper/Modules/Identity/Records/RoleRecord.cs
index 7686271f06..a1f912258c 100644
--- a/src/modules/Elsa.Dapper/Modules/Identity/Records/RoleRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Identity/Records/RoleRecord.cs
@@ -1,8 +1,9 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Identity.Records;
-internal class RoleRecord
+internal class RoleRecord : Record
{
- public string Id { get; set; } = default!;
public string Name { get; set; } = default!;
public string Permissions { get; set; } = default!;
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Identity/Records/UserRecord.cs b/src/modules/Elsa.Dapper/Modules/Identity/Records/UserRecord.cs
index 381f537226..4438bcfc98 100644
--- a/src/modules/Elsa.Dapper/Modules/Identity/Records/UserRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Identity/Records/UserRecord.cs
@@ -1,8 +1,9 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Identity.Records;
-internal class UserRecord
+internal class UserRecord : Record
{
- public string Id { get; set; } = default!;
public string Name { get; set; } = default!;
public string HashedPassword { get; set; } = default!;
public string HashedPasswordSalt { get; set; } = default!;
diff --git a/src/modules/Elsa.Dapper/Modules/Identity/Stores/ApplicationStore.cs b/src/modules/Elsa.Dapper/Modules/Identity/Stores/ApplicationStore.cs
index 88871d2574..f63d4db009 100644
--- a/src/modules/Elsa.Dapper/Modules/Identity/Stores/ApplicationStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Identity/Stores/ApplicationStore.cs
@@ -1,4 +1,3 @@
-using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Identity.Records;
@@ -12,36 +11,25 @@ namespace Elsa.Dapper.Modules.Identity.Stores;
///
/// A Dapper implementation of .
///
-public class DapperApplicationStore : IApplicationStore
+internal class DapperApplicationStore(Store store) : IApplicationStore
{
- private const string TableName = "Applications";
- private readonly Store _store;
-
- ///
- /// Initializes a new instance of .
- ///
- public DapperApplicationStore(IDbConnectionProvider dbConnectionProvider)
- {
- _store = new Store(dbConnectionProvider, TableName);
- }
-
///
public async Task SaveAsync(Application application, CancellationToken cancellationToken = default)
{
var record = Map(application);
- await _store.SaveAsync(record, cancellationToken);
+ await store.SaveAsync(record, cancellationToken);
}
///
public async Task DeleteAsync(ApplicationFilter filter, CancellationToken cancellationToken = default)
{
- await _store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
+ await store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}
///
public async Task FindAsync(ApplicationFilter filter, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(query => ApplyFilter(query, filter), cancellationToken);
+ var record = await store.FindAsync(query => ApplyFilter(query, filter), cancellationToken);
return record == null ? null : Map(record);
}
@@ -54,33 +42,35 @@ private void ApplyFilter(ParameterizedQuery query, ApplicationFilter filter)
;
}
- private ApplicationRecord Map(Application application)
+ private ApplicationRecord Map(Application source)
{
return new()
{
- Id = application.Id,
- ClientId = application.ClientId,
- HashedClientSecret = application.HashedClientSecret,
- HashedClientSecretSalt = application.HashedClientSecretSalt,
- Name = application.Name,
- HashedApiKey = application.HashedApiKey,
- HashedApiKeySalt = application.HashedApiKeySalt,
- Roles = string.Join(',', application.Roles)
+ Id = source.Id,
+ ClientId = source.ClientId,
+ HashedClientSecret = source.HashedClientSecret,
+ HashedClientSecretSalt = source.HashedClientSecretSalt,
+ Name = source.Name,
+ HashedApiKey = source.HashedApiKey,
+ HashedApiKeySalt = source.HashedApiKeySalt,
+ Roles = string.Join(',', source.Roles),
+ TenantId = source.TenantId
};
}
- private Application Map(ApplicationRecord record)
+ private Application Map(ApplicationRecord source)
{
return new()
{
- Id = record.Id,
- ClientId = record.ClientId,
- HashedClientSecret = record.HashedClientSecret,
- HashedClientSecretSalt = record.HashedClientSecretSalt,
- Name = record.Name,
- HashedApiKey = record.HashedApiKey,
- HashedApiKeySalt = record.HashedApiKeySalt,
- Roles = record.Roles.Split(',', StringSplitOptions.RemoveEmptyEntries)
+ Id = source.Id,
+ ClientId = source.ClientId,
+ HashedClientSecret = source.HashedClientSecret,
+ HashedClientSecretSalt = source.HashedClientSecretSalt,
+ Name = source.Name,
+ HashedApiKey = source.HashedApiKey,
+ HashedApiKeySalt = source.HashedApiKeySalt,
+ Roles = source.Roles.Split(',', StringSplitOptions.RemoveEmptyEntries),
+ TenantId = source.TenantId
};
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Identity/Stores/RoleStore.cs b/src/modules/Elsa.Dapper/Modules/Identity/Stores/RoleStore.cs
index 50cd5bbc52..4787bd4dac 100644
--- a/src/modules/Elsa.Dapper/Modules/Identity/Stores/RoleStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Identity/Stores/RoleStore.cs
@@ -1,4 +1,3 @@
-using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Identity.Records;
@@ -13,50 +12,39 @@ namespace Elsa.Dapper.Modules.Identity.Stores;
///
/// A Dapper implementation of .
///
-public class DapperRoleStore : IRoleStore
+internal class DapperRoleStore(Store store) : IRoleStore
{
- private const string TableName = "Roles";
- private readonly Store _store;
-
- ///
- /// Initializes a new instance of .
- ///
- public DapperRoleStore(IDbConnectionProvider dbConnectionProvider)
- {
- _store = new Store(dbConnectionProvider, TableName);
- }
-
///
public async Task SaveAsync(Role application, CancellationToken cancellationToken = default)
{
var record = Map(application);
- await _store.SaveAsync(record, cancellationToken);
+ await store.SaveAsync(record, cancellationToken);
}
///
public async Task AddAsync(Role role, CancellationToken cancellationToken = default)
{
var record = Map(role);
- await _store.AddAsync(record, cancellationToken);
+ await store.AddAsync(record, cancellationToken);
}
///
public async Task DeleteAsync(RoleFilter filter, CancellationToken cancellationToken = default)
{
- await _store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
+ await store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}
///
public async Task FindAsync(RoleFilter filter, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
return record == null ? null : Map(record);
}
///
public async Task> FindManyAsync(RoleFilter filter, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(queryable => ApplyFilter(queryable, filter), cancellationToken).ToList();
+ var records = await store.FindManyAsync(queryable => ApplyFilter(queryable, filter), cancellationToken).ToList();
return records.Select(Map);
}
@@ -74,7 +62,8 @@ private RoleRecord Map(Role source)
{
Id = source.Id,
Name = source.Name,
- Permissions = string.Join(',', source.Permissions)
+ Permissions = string.Join(',', source.Permissions),
+ TenantId = source.TenantId
};
}
@@ -84,7 +73,8 @@ private Role Map(RoleRecord source)
{
Id = source.Id,
Name = source.Name,
- Permissions = source.Permissions.Split(',', StringSplitOptions.RemoveEmptyEntries)
+ Permissions = source.Permissions.Split(',', StringSplitOptions.RemoveEmptyEntries),
+ TenantId = source.TenantId
};
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Identity/Stores/UserStore.cs b/src/modules/Elsa.Dapper/Modules/Identity/Stores/UserStore.cs
index 16faa0f501..23a13c47e6 100644
--- a/src/modules/Elsa.Dapper/Modules/Identity/Stores/UserStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Identity/Stores/UserStore.cs
@@ -1,4 +1,3 @@
-using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Identity.Records;
@@ -12,36 +11,25 @@ namespace Elsa.Dapper.Modules.Identity.Stores;
///
/// A Dapper implementation of .
///
-public class DapperUserStore : IUserStore
+internal class DapperUserStore(Store store) : IUserStore
{
- private const string TableName = "Users";
- private readonly Store _store;
-
- ///
- /// Initializes a new instance of .
- ///
- public DapperUserStore(IDbConnectionProvider dbConnectionProvider)
- {
- _store = new Store(dbConnectionProvider, TableName);
- }
-
///
public async Task SaveAsync(User user, CancellationToken cancellationToken = default)
{
var record = Map(user);
- await _store.SaveAsync(record, cancellationToken);
+ await store.SaveAsync(record, cancellationToken);
}
///
public async Task DeleteAsync(UserFilter filter, CancellationToken cancellationToken = default)
{
- await _store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
+ await store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}
///
public async Task FindAsync(UserFilter filter, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
return record == null ? null : Map(record);
}
@@ -53,27 +41,29 @@ private static void ApplyFilter(ParameterizedQuery query, UserFilter filter)
;
}
- private UserRecord Map(User user)
+ private UserRecord Map(User source)
{
return new()
{
- Id = user.Id,
- Name = user.Name,
- HashedPassword = user.HashedPassword,
- HashedPasswordSalt = user.HashedPasswordSalt,
- Roles = string.Join(',', user.Roles)
+ Id = source.Id,
+ Name = source.Name,
+ HashedPassword = source.HashedPassword,
+ HashedPasswordSalt = source.HashedPasswordSalt,
+ Roles = string.Join(',', source.Roles),
+ TenantId = source.TenantId
};
}
- private User Map(UserRecord user)
+ private User Map(UserRecord source)
{
return new()
{
- Id = user.Id,
- Name = user.Name,
- HashedPassword = user.HashedPassword,
- HashedPasswordSalt = user.HashedPasswordSalt,
- Roles = user.Roles.Split(',', StringSplitOptions.RemoveEmptyEntries)
+ Id = source.Id,
+ Name = source.Name,
+ HashedPassword = source.HashedPassword,
+ HashedPasswordSalt = source.HashedPasswordSalt,
+ Roles = source.Roles.Split(',', StringSplitOptions.RemoveEmptyEntries),
+ TenantId = source.TenantId
};
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowDefinitionPersistenceFeature.cs b/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowDefinitionPersistenceFeature.cs
index d73d0388f3..aed0b03e4e 100644
--- a/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowDefinitionPersistenceFeature.cs
+++ b/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowDefinitionPersistenceFeature.cs
@@ -1,4 +1,6 @@
+using Elsa.Dapper.Extensions;
using Elsa.Dapper.Features;
+using Elsa.Dapper.Modules.Management.Records;
using Elsa.Dapper.Modules.Management.Stores;
using Elsa.Features.Abstractions;
using Elsa.Features.Attributes;
@@ -34,6 +36,6 @@ public override void Apply()
{
base.Apply();
- Services.AddScoped();
+ Services.AddDapperStore("WorkflowDefinitions");
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowInstancePersistenceFeature.cs b/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowInstancePersistenceFeature.cs
index 4a65f875d6..83ba9c740c 100644
--- a/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowInstancePersistenceFeature.cs
+++ b/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowInstancePersistenceFeature.cs
@@ -25,12 +25,4 @@ public override void Configure()
{
Module.Configure(feature => { feature.WorkflowInstanceStore = sp => sp.GetRequiredService(); });
}
-
- ///
- public override void Apply()
- {
- base.Apply();
-
- Services.AddScoped();
- }
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowManagementPersistenceFeature.cs b/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowManagementPersistenceFeature.cs
index 0e6aa3acef..fa4d4f03e5 100644
--- a/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowManagementPersistenceFeature.cs
+++ b/src/modules/Elsa.Dapper/Modules/Management/Features/DapperWorkflowManagementPersistenceFeature.cs
@@ -1,4 +1,6 @@
+using Elsa.Dapper.Extensions;
using Elsa.Dapper.Features;
+using Elsa.Dapper.Modules.Management.Records;
using Elsa.Dapper.Modules.Management.Stores;
using Elsa.Features.Abstractions;
using Elsa.Features.Attributes;
@@ -36,7 +38,7 @@ public override void Apply()
{
base.Apply();
- Services.AddScoped();
- Services.AddScoped();
+ Services.AddDapperStore("WorkflowInstances");
+ Services.AddDapperStore("WorkflowDefinitions");
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Management/Records/WorkflowDefinitionRecord.cs b/src/modules/Elsa.Dapper/Modules/Management/Records/WorkflowDefinitionRecord.cs
index 6735731c56..0db52eb4ed 100644
--- a/src/modules/Elsa.Dapper/Modules/Management/Records/WorkflowDefinitionRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Management/Records/WorkflowDefinitionRecord.cs
@@ -1,8 +1,9 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Management.Records;
-internal class WorkflowDefinitionRecord
+internal class WorkflowDefinitionRecord : Record
{
- public string Id { get; set; } = default!;
public string DefinitionId { get; set; } = default!;
public string? Name { get; set; }
public string? ToolVersion { get; set; }
diff --git a/src/modules/Elsa.Dapper/Modules/Management/Records/WorkflowInstanceRecord.cs b/src/modules/Elsa.Dapper/Modules/Management/Records/WorkflowInstanceRecord.cs
index acd0bb5fe1..8168fcd953 100644
--- a/src/modules/Elsa.Dapper/Modules/Management/Records/WorkflowInstanceRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Management/Records/WorkflowInstanceRecord.cs
@@ -1,8 +1,9 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Management.Records;
-internal class WorkflowInstanceRecord
+internal class WorkflowInstanceRecord : Record
{
- public string Id { get; set; } = default!;
public string DefinitionId { get; set; } = default!;
public string DefinitionVersionId { get; set; } = default!;
public int Version { get; set; }
diff --git a/src/modules/Elsa.Dapper/Modules/Management/Stores/DapperWorkflowDefinitionStore.cs b/src/modules/Elsa.Dapper/Modules/Management/Stores/DapperWorkflowDefinitionStore.cs
index 972f3d5aeb..b2d841d98d 100644
--- a/src/modules/Elsa.Dapper/Modules/Management/Stores/DapperWorkflowDefinitionStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Management/Stores/DapperWorkflowDefinitionStore.cs
@@ -1,51 +1,40 @@
-using System.Text.Json.Serialization;
using Elsa.Common.Entities;
using Elsa.Common.Models;
-using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Management.Records;
using Elsa.Dapper.Services;
using Elsa.Extensions;
using Elsa.Workflows.Contracts;
-using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Entities;
using Elsa.Workflows.Management.Filters;
using Elsa.Workflows.Management.Models;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
+using JetBrains.Annotations;
+using Newtonsoft.Json;
namespace Elsa.Dapper.Modules.Management.Stores;
///
/// Provides a Dapper implementation of .
///
-public class DapperWorkflowDefinitionStore : IWorkflowDefinitionStore
+[UsedImplicitly]
+internal class DapperWorkflowDefinitionStore(Store store, IPayloadSerializer payloadSerializer)
+ : IWorkflowDefinitionStore
{
- private const string TableName = "WorkflowDefinitions";
- private readonly IPayloadSerializer _payloadSerializer;
- private readonly Store _store;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public DapperWorkflowDefinitionStore(IDbConnectionProvider dbConnectionProvider, IPayloadSerializer payloadSerializer)
- {
- _payloadSerializer = payloadSerializer;
- _store = new Store(dbConnectionProvider, TableName);
- }
-
///
public async Task FindAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
return record == null ? null : Map(record);
}
///
public async Task FindAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
return record == null ? null : Map(record);
}
@@ -62,21 +51,21 @@ public async Task> FindManyAsync(WorkflowDefinitionFilt
///
public async Task> FindManyAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, PageArgs pageArgs, CancellationToken cancellationToken = default)
{
- var page = await _store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ var page = await store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
return Map(page);
}
///
public async Task> FindManyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
return Map(records).ToList();
}
///
public async Task> FindManyAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
return Map(records).ToList();
}
@@ -93,26 +82,26 @@ public async Task> FindSummariesAsync(WorkflowDe
///
public async Task> FindSummariesAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, PageArgs pageArgs, CancellationToken cancellationToken = default)
{
- return await _store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ return await store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
}
///
public async Task> FindSummariesAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
return records.ToList();
}
///
public async Task> FindSummariesAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, CancellationToken cancellationToken = default)
{
- return await _store.FindManyAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ return await store.FindManyAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
}
///
public async Task FindLastVersionAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), nameof(WorkflowDefinitionRecord.Version), OrderDirection.Descending, cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), nameof(WorkflowDefinitionRecord.Version), OrderDirection.Descending, cancellationToken);
return record == null ? null : Map(record);
}
@@ -120,40 +109,40 @@ public async Task> FindSummariesAsync
public async Task SaveManyAsync(IEnumerable definitions, CancellationToken cancellationToken = default)
{
var records = definitions.Select(Map).ToList();
- await _store.SaveManyAsync(records, nameof(WorkflowDefinitionRecord.Id), cancellationToken);
+ await store.SaveManyAsync(records, cancellationToken);
}
///
public async Task DeleteAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default)
{
- return await _store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
+ return await store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}
///
public async Task AnyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default)
{
- return await _store.AnyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ return await store.AnyAsync(q => ApplyFilter(q, filter), cancellationToken);
}
///
public async Task CountDistinctAsync(CancellationToken cancellationToken = default)
{
- return await _store.CountAsync(
- filter => filter.Count($"distinct {nameof(WorkflowDefinition.DefinitionId)}", TableName),
+ return await store.CountAsync(
+ filter => filter.Count($"distinct {nameof(WorkflowDefinition.DefinitionId)}", store.TableName),
cancellationToken);
}
///
public async Task GetIsNameUnique(string name, string? definitionId = default, CancellationToken cancellationToken = default)
{
- var exists = await _store.AnyAsync(query =>
+ var exists = await store.AnyAsync(query =>
{
query.Is(nameof(WorkflowDefinition.Name), name);
@@ -190,7 +179,7 @@ private void ApplyFilter(ParameterizedQuery query, WorkflowDefinitionFilter filt
private WorkflowDefinition Map(WorkflowDefinitionRecord source)
{
- var props = _payloadSerializer.Deserialize(source.Props);
+ var props = payloadSerializer.Deserialize(source.Props);
return new WorkflowDefinition
{
Id = source.Id,
@@ -213,7 +202,8 @@ private WorkflowDefinition Map(WorkflowDefinitionRecord source)
MaterializerContext = source.MaterializerContext,
BinaryData = source.BinaryData,
MaterializerName = source.MaterializerName,
- ProviderName = source.ProviderName
+ ProviderName = source.ProviderName,
+ TenantId = source.TenantId
};
}
@@ -242,12 +232,13 @@ private WorkflowDefinitionRecord Map(WorkflowDefinition source)
IsReadonly = source.IsReadonly,
IsSystem = source.IsSystem,
StringData = source.StringData,
- Props = _payloadSerializer.Serialize(props),
+ Props = payloadSerializer.Serialize(props),
MaterializerName = source.MaterializerName,
UsableAsActivity = source.Options.UsableAsActivity,
ProviderName = source.ProviderName,
BinaryData = source.BinaryData,
- MaterializerContext = source.MaterializerContext
+ MaterializerContext = source.MaterializerContext,
+ TenantId = source.TenantId
};
}
diff --git a/src/modules/Elsa.Dapper/Modules/Management/Stores/DapperWorkflowInstanceStore.cs b/src/modules/Elsa.Dapper/Modules/Management/Stores/DapperWorkflowInstanceStore.cs
index 40c747f74c..075471c8fe 100644
--- a/src/modules/Elsa.Dapper/Modules/Management/Stores/DapperWorkflowInstanceStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Management/Stores/DapperWorkflowInstanceStore.cs
@@ -1,7 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using Elsa.Common.Entities;
using Elsa.Common.Models;
-using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Management.Records;
@@ -9,39 +8,31 @@
using Elsa.Extensions;
using Elsa.Workflows;
using Elsa.Workflows.Contracts;
-using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Entities;
using Elsa.Workflows.Management.Filters;
using Elsa.Workflows.Management.Models;
+using JetBrains.Annotations;
namespace Elsa.Dapper.Modules.Management.Stores;
///
/// Provides a Dapper implementation of .
///
-public class DapperWorkflowInstanceStore : IWorkflowInstanceStore
+[UsedImplicitly]
+internal class DapperWorkflowInstanceStore(Store store, IWorkflowStateSerializer workflowStateSerializer)
+ : IWorkflowInstanceStore
{
- private const string TableName = "WorkflowInstances";
- private readonly IWorkflowStateSerializer _workflowStateSerializer;
- private readonly Store _store;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public DapperWorkflowInstanceStore(IDbConnectionProvider dbConnectionProvider, IWorkflowStateSerializer workflowStateSerializer)
- {
- _workflowStateSerializer = workflowStateSerializer;
- _store = new Store(dbConnectionProvider, TableName);
- }
-
///
+ [RequiresUnreferencedCode("Calls Elsa.Dapper.Modules.Management.Stores.DapperWorkflowInstanceStore.MapAsync(WorkflowInstanceRecord)")]
public async ValueTask FindAsync(WorkflowInstanceFilter filter, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
return record == null ? null : Map(record);
}
///
+ [RequiresUnreferencedCode("Calls Elsa.Dapper.Modules.Management.Stores.DapperWorkflowInstanceStore.FindManyAsync(WorkflowInstanceFilter, PageArgs, WorkflowInstanceOrder, CancellationToken)")]
public async ValueTask> FindManyAsync(WorkflowInstanceFilter filter, PageArgs pageArgs, CancellationToken cancellationToken = default)
{
return await FindManyAsync(
@@ -52,30 +43,33 @@ public async ValueTask> FindManyAsync(WorkflowInstanceFil
}
///
+ [RequiresUnreferencedCode("Calls Elsa.Dapper.Modules.Management.Stores.DapperWorkflowInstanceStore.MapAsync(Page)")]
public async ValueTask> FindManyAsync(WorkflowInstanceFilter filter, PageArgs pageArgs, WorkflowInstanceOrder order, CancellationToken cancellationToken = default)
{
- var page = await _store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ var page = await store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
return Map(page);
}
///
+ [RequiresUnreferencedCode("Calls Elsa.Dapper.Modules.Management.Stores.DapperWorkflowInstanceStore.MapAsync(IEnumerable)")]
public async ValueTask> FindManyAsync(WorkflowInstanceFilter filter, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
return Map(records).ToList();
}
///
+ [RequiresUnreferencedCode("Calls Elsa.Dapper.Modules.Management.Stores.DapperWorkflowInstanceStore.MapAsync(IEnumerable)")]
public async ValueTask> FindManyAsync(WorkflowInstanceFilter filter, WorkflowInstanceOrder order, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
return Map(records).ToList();
}
///
public async ValueTask CountAsync(WorkflowInstanceFilter filter, CancellationToken cancellationToken = default)
{
- return await _store.CountAsync(query => ApplyFilter(query, filter), cancellationToken);
+ return await store.CountAsync(query => ApplyFilter(query, filter), cancellationToken);
}
///
@@ -91,7 +85,7 @@ public async ValueTask> SummarizeManyAsync(Workflo
///
public async ValueTask> SummarizeManyAsync(WorkflowInstanceFilter filter, PageArgs pageArgs, WorkflowInstanceOrder order, CancellationToken cancellationToken = default)
{
- return await _store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ return await store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
}
///
@@ -103,13 +97,13 @@ public async ValueTask> SummarizeManyAsync(
///
public async ValueTask> SummarizeManyAsync(WorkflowInstanceFilter filter, WorkflowInstanceOrder order, CancellationToken cancellationToken = default)
{
- return await _store.FindManyAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ return await store.FindManyAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
}
///
public async ValueTask> FindManyIdsAsync(WorkflowInstanceFilter filter, CancellationToken cancellationToken = default)
{
- var items = await _store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var items = await store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
return items.Select(x => x.Id).ToList();
}
@@ -126,7 +120,7 @@ public async ValueTask> FindManyIdsAsync(WorkflowInstanceFilter fil
///
public async ValueTask> FindManyIdsAsync(WorkflowInstanceFilter filter, PageArgs pageArgs, WorkflowInstanceOrder order, CancellationToken cancellationToken = default)
{
- var page = await _store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ var page = await store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
var ids = page.Items.Select(x => x.Id).ToList();
return Page.Of(ids, page.TotalCount);
}
@@ -136,21 +130,21 @@ public async ValueTask> FindManyIdsAsync(WorkflowInstance
public async ValueTask SaveAsync(WorkflowInstance instance, CancellationToken cancellationToken = default)
{
var record = Map(instance);
- await _store.SaveAsync(record, cancellationToken);
+ await store.SaveAsync(record, cancellationToken);
}
///
public async ValueTask AddAsync(WorkflowInstance instance, CancellationToken cancellationToken = default)
{
var record = Map(instance);
- await _store.AddAsync(record, cancellationToken);
+ await store.AddAsync(record, cancellationToken);
}
///
public async ValueTask UpdateAsync(WorkflowInstance instance, CancellationToken cancellationToken = default)
{
var record = Map(instance);
- await _store.UpdateAsync(record, cancellationToken);
+ await store.UpdateAsync(record, cancellationToken);
}
///
@@ -158,13 +152,13 @@ public async ValueTask UpdateAsync(WorkflowInstance instance, CancellationToken
public async ValueTask SaveManyAsync(IEnumerable instances, CancellationToken cancellationToken = default)
{
var records = Map(instances);
- await _store.SaveManyAsync(records, cancellationToken);
+ await store.SaveManyAsync(records, cancellationToken);
}
///
public async ValueTask DeleteAsync(WorkflowInstanceFilter filter, CancellationToken cancellationToken = default)
{
- return await _store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
+ return await store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}
private void ApplyFilter(ParameterizedQuery query, WorkflowInstanceFilter filter)
@@ -209,7 +203,7 @@ private IEnumerable Map(IEnumerable so
[RequiresUnreferencedCode("Calls Elsa.Workflows.Contracts.IWorkflowStateSerializer.DeserializeAsync(String, CancellationToken)")]
private WorkflowInstance Map(WorkflowInstanceRecord source)
{
- var workflowState = _workflowStateSerializer.Deserialize(source.WorkflowState);
+ var workflowState = workflowStateSerializer.Deserialize(source.WorkflowState);
return new WorkflowInstance
{
Id = source.Id,
@@ -226,13 +220,14 @@ private WorkflowInstance Map(WorkflowInstanceRecord source)
Status = Enum.Parse(source.Status),
SubStatus = Enum.Parse(source.SubStatus),
CorrelationId = source.CorrelationId,
+ TenantId = source.TenantId
};
}
[RequiresUnreferencedCode("Calls Elsa.Workflows.Contracts.IWorkflowStateSerializer.DeserializeAsync(String, CancellationToken)")]
private WorkflowInstanceRecord Map(WorkflowInstance source)
{
- var workflowState = _workflowStateSerializer.Serialize(source.WorkflowState);
+ var workflowState = workflowStateSerializer.Serialize(source.WorkflowState);
return new WorkflowInstanceRecord
{
Id = source.Id,
@@ -248,7 +243,8 @@ private WorkflowInstanceRecord Map(WorkflowInstance source)
FinishedAt = source.FinishedAt,
Status = source.Status.ToString(),
SubStatus = source.SubStatus.ToString(),
- CorrelationId = source.CorrelationId
+ CorrelationId = source.CorrelationId,
+ TenantId = source.TenantId
};
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Features/DapperWorkflowRuntimePersistenceFeature.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Features/DapperWorkflowRuntimePersistenceFeature.cs
index bd77bbc47e..6ef3bae1ed 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Features/DapperWorkflowRuntimePersistenceFeature.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Features/DapperWorkflowRuntimePersistenceFeature.cs
@@ -1,4 +1,6 @@
+using Elsa.Dapper.Extensions;
using Elsa.Dapper.Features;
+using Elsa.Dapper.Modules.Runtime.Records;
using Elsa.Dapper.Modules.Runtime.Stores;
using Elsa.Features.Abstractions;
using Elsa.Features.Attributes;
@@ -43,10 +45,10 @@ public override void Apply()
{
base.Apply();
- Services.AddScoped();
- Services.AddScoped();
- Services.AddScoped();
- Services.AddScoped();
- Services.AddScoped();
+ Services.AddDapperStore("Triggers");
+ Services.AddDapperStore("Bookmarks");
+ Services.AddDapperStore("WorkflowExecutionLogRecords");
+ Services.AddDapperStore("ActivityExecutionRecords");
+ Services.AddDapperStore("KeyValues");
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs
index ee98a3aa2f..9887a92bc3 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs
@@ -1,15 +1,12 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Runtime.Records;
///
/// Represents a single workflow execution, associated with an individual activity instance.
///
-public class ActivityExecutionRecordRecord
+internal class ActivityExecutionRecordRecord : Record
{
- ///
- /// Gets or sets the ID of the activity execution record.
- ///
- public string Id { get; set; } = default!;
-
///
/// Gets or sets the workflow instance ID.
///
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs
index 6a7dd30770..0e389202bc 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs
@@ -1,15 +1,12 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Runtime.Records;
///
/// Represents a single workflow execution, associated with an individual activity instance.
///
-public class ActivityExecutionSummaryRecord
+internal class ActivityExecutionSummaryRecord : Record
{
- ///
- /// Gets or sets the ID of the activity execution record.
- ///
- public string Id { get; set; } = default!;
-
///
/// Gets or sets the workflow instance ID.
///
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs
index 64730f781c..a1d97edff1 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs
@@ -1,7 +1,8 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Runtime.Records;
-public class KeyValuePairRecord
+internal class KeyValuePairRecord : Record
{
- public string Key { get; set; } = default!;
public string Value { get; set; } = default!;
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs
index caa1a9c9b5..ecaf036520 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs
@@ -1,8 +1,9 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Runtime.Records;
-internal class StoredBookmarkRecord
+internal class StoredBookmarkRecord : Record
{
- public string Id { get; set; } = default!;
public string ActivityTypeName { get; set; } = default!;
public string Hash { get; set; } = default!;
public string WorkflowInstanceId { get; set; } = default!;
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs
index 0f190c3daa..c81a8318c7 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs
@@ -1,8 +1,9 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Runtime.Records;
-internal class StoredTriggerRecord
+internal class StoredTriggerRecord : Record
{
- public string Id { get; set; } = default!;
public string WorkflowDefinitionId { get; set; } = default!;
public string WorkflowDefinitionVersionId { get; set; } = default!;
public string Name { get; set; } = default!;
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs
index 6a3fa00023..60115f2867 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs
@@ -1,6 +1,8 @@
+using Elsa.Dapper.Records;
+
namespace Elsa.Dapper.Modules.Runtime.Records;
-internal class WorkflowExecutionLogRecordRecord
+internal class WorkflowExecutionLogRecordRecord : Record
{
public string Id { get; set; } = default!;
public string WorkflowDefinitionId { get; set; } = default!;
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs
index 3f33a49f44..f5742b4725 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs
@@ -1,4 +1,3 @@
-using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Runtime.Records;
@@ -12,89 +11,76 @@
using Elsa.Workflows.Runtime.Filters;
using Elsa.Workflows.Runtime.OrderDefinitions;
using Elsa.Workflows.State;
+using JetBrains.Annotations;
namespace Elsa.Dapper.Modules.Runtime.Stores;
///
/// Implements the using Dapper.
///
-public class DapperActivityExecutionRecordStore : IActivityExecutionStore
+[UsedImplicitly]
+internal class DapperActivityExecutionRecordStore(Store store, IPayloadSerializer payloadSerializer, ISafeSerializer safeSerializer)
+ : IActivityExecutionStore
{
- private const string TableName = "ActivityExecutionRecords";
- private const string PrimaryKeyName = "Id";
- private readonly IPayloadSerializer _payloadSerializer;
- private readonly ISafeSerializer _safeSerializer;
- private readonly Store _store;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public DapperActivityExecutionRecordStore(IDbConnectionProvider dbConnectionProvider, IPayloadSerializer payloadSerializer, ISafeSerializer safeSerializer)
- {
- _payloadSerializer = payloadSerializer;
- _safeSerializer = safeSerializer;
- _store = new Store(dbConnectionProvider, TableName, PrimaryKeyName);
- }
-
///
public async Task SaveAsync(ActivityExecutionRecord record, CancellationToken cancellationToken = default)
{
var mappedRecord = await Map(record, cancellationToken);
- await _store.SaveAsync(mappedRecord, PrimaryKeyName, cancellationToken);
+ await store.SaveAsync(mappedRecord, cancellationToken);
}
///
public async Task SaveManyAsync(IEnumerable records, CancellationToken cancellationToken = default)
{
var mappedRecords = await Task.WhenAll(records.Select(async x => await Map(x, cancellationToken)));
- await _store.SaveManyAsync(mappedRecords, PrimaryKeyName, cancellationToken);
+ await store.SaveManyAsync(mappedRecords, cancellationToken);
}
///
public async Task FindAsync(ActivityExecutionRecordFilter filter, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
return record == null ? null : await MapAsync(record, cancellationToken);
}
///
public async Task> FindManyAsync(ActivityExecutionRecordFilter filter, ActivityExecutionRecordOrder order, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
return await Task.WhenAll(records.Select(async x => await MapAsync(x, cancellationToken)));
}
///
public async Task> FindManyAsync(ActivityExecutionRecordFilter filter, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
return await Task.WhenAll(records.Select(async x => await MapAsync(x, cancellationToken)));
}
///
public async Task> FindManySummariesAsync(ActivityExecutionRecordFilter filter, ActivityExecutionRecordOrder order, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
return records.Select(MapSummary).ToList();
}
///
public async Task> FindManySummariesAsync(ActivityExecutionRecordFilter filter, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
return records.Select(MapSummary).ToList();
}
///
public async Task CountAsync(ActivityExecutionRecordFilter filter, CancellationToken cancellationToken = default)
{
- return await _store.CountAsync(q => ApplyFilter(q, filter), cancellationToken);
+ return await store.CountAsync(q => ApplyFilter(q, filter), cancellationToken);
}
///
public async Task DeleteManyAsync(ActivityExecutionRecordFilter filter, CancellationToken cancellationToken = default)
{
- return await _store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
+ return await store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}
private static void ApplyFilter(ParameterizedQuery query, ActivityExecutionRecordFilter filter)
@@ -131,10 +117,11 @@ private async ValueTask Map(ActivityExecutionReco
HasBookmarks = source.HasBookmarks,
Status = source.Status.ToString(),
ActivityTypeVersion = source.ActivityTypeVersion,
- SerializedActivityState = source.ActivityState != null ? await _safeSerializer.SerializeAsync(source.ActivityState, cancellationToken) : default,
- SerializedPayload = source.Payload != null ? await _safeSerializer.SerializeAsync(source.Payload, cancellationToken) : default,
- SerializedOutputs = source.Outputs != null ? await _safeSerializer.SerializeAsync(source.Outputs, cancellationToken) : default,
- SerializedException = source.Exception != null ? _payloadSerializer.Serialize(source.Exception) : default
+ SerializedActivityState = source.ActivityState != null ? await safeSerializer.SerializeAsync(source.ActivityState, cancellationToken) : default,
+ SerializedPayload = source.Payload != null ? await safeSerializer.SerializeAsync(source.Payload, cancellationToken) : default,
+ SerializedOutputs = source.Outputs != null ? await safeSerializer.SerializeAsync(source.Outputs, cancellationToken) : default,
+ SerializedException = source.Exception != null ? payloadSerializer.Serialize(source.Exception) : default,
+ TenantId = source.TenantId
};
}
@@ -153,10 +140,11 @@ private async ValueTask MapAsync(ActivityExecutionRecor
HasBookmarks = source.HasBookmarks,
Status = Enum.Parse(source.Status),
ActivityTypeVersion = source.ActivityTypeVersion,
- ActivityState = source.SerializedActivityState != null ? _payloadSerializer.Deserialize>(source.SerializedActivityState) : default,
- Payload = source.SerializedPayload != null ? await _safeSerializer.DeserializeAsync>(source.SerializedPayload, cancellationToken) : default,
- Outputs = source.SerializedOutputs != null ? await _safeSerializer.DeserializeAsync>(source.SerializedOutputs, cancellationToken) : default,
- Exception = source.SerializedException != null ? _payloadSerializer.Deserialize(source.SerializedException) : default
+ ActivityState = source.SerializedActivityState != null ? payloadSerializer.Deserialize>(source.SerializedActivityState) : default,
+ Payload = source.SerializedPayload != null ? await safeSerializer.DeserializeAsync>(source.SerializedPayload, cancellationToken) : default,
+ Outputs = source.SerializedOutputs != null ? await safeSerializer.DeserializeAsync>(source.SerializedOutputs, cancellationToken) : default,
+ Exception = source.SerializedException != null ? payloadSerializer.Deserialize(source.SerializedException) : default,
+ TenantId = source.TenantId
};
}
@@ -174,7 +162,8 @@ private ActivityExecutionRecordSummary MapSummary(ActivityExecutionSummaryRecord
StartedAt = source.StartedAt,
HasBookmarks = source.HasBookmarks,
Status = Enum.Parse(source.Status),
- ActivityTypeVersion = source.ActivityTypeVersion
+ ActivityTypeVersion = source.ActivityTypeVersion,
+ TenantId = source.TenantId
};
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperBookmarkStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperBookmarkStore.cs
index 06372b80cf..cc1f393dc0 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperBookmarkStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperBookmarkStore.cs
@@ -1,4 +1,3 @@
-using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Runtime.Records;
@@ -8,59 +7,48 @@
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
+using JetBrains.Annotations;
namespace Elsa.Dapper.Modules.Runtime.Stores;
///
/// A Dapper-based implementation.
///
-public class DapperBookmarkStore : IBookmarkStore
+[UsedImplicitly]
+internal class DapperBookmarkStore(Store store, IPayloadSerializer payloadSerializer) : IBookmarkStore
{
- private readonly IPayloadSerializer _payloadSerializer;
- private const string TableName = "Bookmarks";
- private readonly Store _store;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public DapperBookmarkStore(IDbConnectionProvider dbConnectionProvider, IPayloadSerializer payloadSerializer)
- {
- _payloadSerializer = payloadSerializer;
- _store = new Store(dbConnectionProvider, TableName);
- }
-
///
public async ValueTask SaveAsync(StoredBookmark record, CancellationToken cancellationToken = default)
{
var mappedRecord = Map(record);
- await _store.SaveAsync(mappedRecord, cancellationToken);
+ await store.SaveAsync(mappedRecord, cancellationToken);
}
///
public async ValueTask SaveManyAsync(IEnumerable records, CancellationToken cancellationToken)
{
var mappedRecords = Map(records);
- await _store.SaveManyAsync(mappedRecords, cancellationToken);
+ await store.SaveManyAsync(mappedRecords, cancellationToken);
}
///
public async ValueTask FindAsync(BookmarkFilter filter, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
return record != null ? Map(record) : default;
}
///
public async ValueTask> FindManyAsync(BookmarkFilter filter, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), filter.TenantAgnostic, cancellationToken);
return Map(records);
}
///
public async ValueTask DeleteAsync(BookmarkFilter filter, CancellationToken cancellationToken = default)
{
- return await _store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
+ return await store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}
private void ApplyFilter(ParameterizedQuery query, BookmarkFilter filter)
@@ -84,15 +72,16 @@ private StoredBookmarkRecord Map(StoredBookmark source)
{
return new StoredBookmarkRecord
{
- Id = source.BookmarkId,
+ Id = source.Id,
WorkflowInstanceId = source.WorkflowInstanceId,
CorrelationId = source.CorrelationId,
ActivityInstanceId = source.ActivityInstanceId,
ActivityTypeName = source.ActivityTypeName,
Hash = source.Hash,
- SerializedPayload = source.Payload != null ? _payloadSerializer.Serialize(source.Payload) : default,
- SerializedMetadata = source.Metadata != null ? _payloadSerializer.Serialize(source.Metadata) : default,
- CreatedAt = source.CreatedAt
+ SerializedPayload = source.Payload != null ? payloadSerializer.Serialize(source.Payload) : default,
+ SerializedMetadata = source.Metadata != null ? payloadSerializer.Serialize(source.Metadata) : default,
+ CreatedAt = source.CreatedAt,
+ TenantId = source.TenantId
};
}
@@ -100,15 +89,16 @@ private StoredBookmark Map(StoredBookmarkRecord source)
{
return new StoredBookmark
{
- BookmarkId = source.Id,
+ Id = source.Id,
WorkflowInstanceId = source.WorkflowInstanceId,
CorrelationId = source.CorrelationId,
ActivityInstanceId = source.ActivityInstanceId,
ActivityTypeName = source.ActivityTypeName,
Hash = source.Hash,
- Payload = source.SerializedPayload != null ? _payloadSerializer.Deserialize(source.SerializedPayload) : default,
- Metadata = source.SerializedMetadata != null ? _payloadSerializer.Deserialize>(source.SerializedMetadata) : default,
- CreatedAt = source.CreatedAt
+ Payload = source.SerializedPayload != null ? payloadSerializer.Deserialize(source.SerializedPayload) : default,
+ Metadata = source.SerializedMetadata != null ? payloadSerializer.Deserialize>(source.SerializedMetadata) : default,
+ CreatedAt = source.CreatedAt,
+ TenantId = source.TenantId
};
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperTriggerStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperTriggerStore.cs
index 528c916163..507b313478 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperTriggerStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperTriggerStore.cs
@@ -1,5 +1,4 @@
-using Elsa.Dapper.Contracts;
-using Elsa.Dapper.Extensions;
+using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Runtime.Records;
using Elsa.Dapper.Services;
@@ -8,59 +7,51 @@
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
+using JetBrains.Annotations;
namespace Elsa.Dapper.Modules.Runtime.Stores;
///
/// Provides a Dapper implementation of .
///
-public class DapperTriggerStore : ITriggerStore
+[UsedImplicitly]
+internal class DapperTriggerStore(Store store, IPayloadSerializer payloadSerializer) : ITriggerStore
{
- private const string TableName = "Triggers";
- private readonly IPayloadSerializer _payloadSerializer;
- private readonly Store _store;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public DapperTriggerStore(IDbConnectionProvider dbConnectionProvider, IPayloadSerializer payloadSerializer)
- {
- _payloadSerializer = payloadSerializer;
- _store = new Store(dbConnectionProvider, TableName);
- }
-
///
public async ValueTask SaveAsync(StoredTrigger record, CancellationToken cancellationToken = default)
{
var mappedRecord = Map(record);
- await _store.SaveAsync(mappedRecord, cancellationToken);
+ await store.SaveAsync(mappedRecord, cancellationToken);
}
///
public async ValueTask SaveManyAsync(IEnumerable records, CancellationToken cancellationToken = default)
{
var mappedRecords = records.Select(Map);
- await _store.SaveManyAsync(mappedRecords, cancellationToken);
+ await store.SaveManyAsync(mappedRecords, cancellationToken);
}
///
public async ValueTask FindAsync(TriggerFilter filter, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
return record != null ? Map(record) : default;
}
///
public async ValueTask> FindManyAsync(TriggerFilter filter, CancellationToken cancellationToken = default)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
return Map(records);
}
///
public async ValueTask ReplaceAsync(IEnumerable removed, IEnumerable added, CancellationToken cancellationToken = default)
{
- var filter = new TriggerFilter { Ids = removed.Select(r => r.Id).ToList() };
+ var filter = new TriggerFilter
+ {
+ Ids = removed.Select(r => r.Id).ToList()
+ };
await DeleteManyAsync(filter, cancellationToken);
await SaveManyAsync(added, cancellationToken);
}
@@ -68,7 +59,7 @@ public async ValueTask ReplaceAsync(IEnumerable removed, IEnumera
///
public async ValueTask DeleteManyAsync(TriggerFilter filter, CancellationToken cancellationToken = default)
{
- return await _store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
+ return await store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}
private void ApplyFilter(ParameterizedQuery query, TriggerFilter filter)
@@ -98,7 +89,8 @@ private StoredTrigger Map(StoredTriggerRecord source)
Name = source.Name,
WorkflowDefinitionId = source.WorkflowDefinitionId,
WorkflowDefinitionVersionId = source.WorkflowDefinitionVersionId,
- Payload = source.SerializedPayload != null ? _payloadSerializer.Deserialize(source.SerializedPayload) : default
+ Payload = source.SerializedPayload != null ? payloadSerializer.Deserialize(source.SerializedPayload) : default,
+ TenantId = source.TenantId
};
}
@@ -112,7 +104,8 @@ private StoredTriggerRecord Map(StoredTrigger source)
Name = source.Name,
WorkflowDefinitionId = source.WorkflowDefinitionId,
WorkflowDefinitionVersionId = source.WorkflowDefinitionVersionId,
- SerializedPayload = source.Payload != null ? _payloadSerializer.Serialize(source.Payload) : default
+ SerializedPayload = source.Payload != null ? payloadSerializer.Serialize(source.Payload) : default,
+ TenantId = source.TenantId
};
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperWorkflowExecutionLogStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperWorkflowExecutionLogStore.cs
index 34ad39393e..5d497f9bd7 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperWorkflowExecutionLogStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperWorkflowExecutionLogStore.cs
@@ -1,6 +1,5 @@
using Elsa.Common.Entities;
using Elsa.Common.Models;
-using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Runtime.Records;
@@ -12,66 +11,55 @@
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
using Elsa.Workflows.Runtime.OrderDefinitions;
+using JetBrains.Annotations;
namespace Elsa.Dapper.Modules.Runtime.Stores;
///
/// Implements the using Dapper.
///
-public class DapperWorkflowExecutionLogStore : IWorkflowExecutionLogStore
+[UsedImplicitly]
+internal class DapperWorkflowExecutionLogStore(Store store, IPayloadSerializer payloadSerializer) : IWorkflowExecutionLogStore
{
- private const string TableName = "WorkflowExecutionLogRecords";
- private readonly IPayloadSerializer _payloadSerializer;
- private readonly Store _store;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public DapperWorkflowExecutionLogStore(IDbConnectionProvider dbConnectionProvider, IPayloadSerializer payloadSerializer)
- {
- _payloadSerializer = payloadSerializer;
- _store = new Store(dbConnectionProvider, TableName);
- }
-
///
public async Task AddAsync(WorkflowExecutionLogRecord record, CancellationToken cancellationToken = default)
{
var mappedRecord = Map(record);
- await _store.AddAsync(mappedRecord, cancellationToken);
+ await store.AddAsync(mappedRecord, cancellationToken);
}
///
public async Task AddManyAsync(IEnumerable records, CancellationToken cancellationToken = default)
{
var mappedRecords = records.Select(Map);
- await _store.AddManyAsync(mappedRecords, cancellationToken);
+ await store.AddManyAsync(mappedRecords, cancellationToken);
}
///
public async Task SaveAsync(WorkflowExecutionLogRecord record, CancellationToken cancellationToken = default)
{
var mappedRecord = Map(record);
- await _store.SaveAsync(mappedRecord, cancellationToken);
+ await store.SaveAsync(mappedRecord, cancellationToken);
}
///
public async Task SaveManyAsync(IEnumerable records, CancellationToken cancellationToken = default)
{
var mappedRecords = records.Select(Map);
- await _store.SaveManyAsync(mappedRecords, cancellationToken);
+ await store.SaveManyAsync(mappedRecords, cancellationToken);
}
///
public async Task FindAsync(WorkflowExecutionLogRecordFilter filter, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
return record != null ? Map(record) : default;
}
///
public async Task FindAsync(WorkflowExecutionLogRecordFilter filter, WorkflowExecutionLogRecordOrder order, CancellationToken cancellationToken = default)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
return record != null ? Map(record) : default;
}
@@ -88,14 +76,14 @@ public async Task> FindManyAsync(WorkflowExecut
///
public async Task> FindManyAsync(WorkflowExecutionLogRecordFilter filter, PageArgs pageArgs, WorkflowExecutionLogRecordOrder order, CancellationToken cancellationToken = default)
{
- var page = await _store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
+ var page = await store.FindManyAsync(q => ApplyFilter(q, filter), pageArgs, order.KeySelector.GetPropertyName(), order.Direction, cancellationToken);
return Map(page);
}
///
public async Task DeleteManyAsync(WorkflowExecutionLogRecordFilter filter, CancellationToken cancellationToken = default)
{
- return await _store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
+ return await store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}
private static void ApplyFilter(ParameterizedQuery query, WorkflowExecutionLogRecordFilter filter)
@@ -139,8 +127,9 @@ private WorkflowExecutionLogRecordRecord Map(WorkflowExecutionLogRecord source)
EventName = source.EventName,
Message = source.Message,
Source = source.Source,
- SerializedActivityState = source.ActivityState != null ? _payloadSerializer.Serialize(source.ActivityState) : null,
- SerializedPayload = source.Payload != null ? _payloadSerializer.Serialize(source.Payload) : null,
+ SerializedActivityState = source.ActivityState != null ? payloadSerializer.Serialize(source.ActivityState) : null,
+ SerializedPayload = source.Payload != null ? payloadSerializer.Serialize(source.Payload) : null,
+ TenantId = source.TenantId
};
}
@@ -165,8 +154,9 @@ private WorkflowExecutionLogRecord Map(WorkflowExecutionLogRecordRecord source)
EventName = source.EventName,
Message = source.Message,
Source = source.Source,
- ActivityState = source.SerializedActivityState != null ? _payloadSerializer.Deserialize>(source.SerializedActivityState) : null,
- Payload = source.SerializedPayload != null ? _payloadSerializer.Deserialize(source.SerializedPayload) : null,
+ ActivityState = source.SerializedActivityState != null ? payloadSerializer.Deserialize>(source.SerializedActivityState) : null,
+ Payload = source.SerializedPayload != null ? payloadSerializer.Deserialize(source.SerializedPayload) : null,
+ TenantId = source.TenantId
};
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs
index 3eb19191e2..7323281df1 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs
@@ -1,4 +1,3 @@
-using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Runtime.Records;
@@ -6,76 +5,68 @@
using Elsa.KeyValues.Contracts;
using Elsa.KeyValues.Entities;
using Elsa.KeyValues.Models;
+using JetBrains.Annotations;
namespace Elsa.Dapper.Modules.Runtime.Stores;
///
/// A Dapper implementation of .
///
-public class DapperKeyValueStore : IKeyValueStore
+[UsedImplicitly]
+internal class DapperKeyValueStore(Store store) : IKeyValueStore
{
- private const string TableName = "Users";
- private const string PrimaryKeyName = "Key";
- private readonly Store _store;
-
- ///
- /// Initializes a new instance of .
- ///
- public DapperKeyValueStore(IDbConnectionProvider dbConnectionProvider)
- {
- _store = new Store(dbConnectionProvider, TableName, PrimaryKeyName);
- }
-
///
public Task SaveAsync(SerializedKeyValuePair keyValuePair, CancellationToken cancellationToken)
{
var record = Map(keyValuePair);
- return _store.SaveAsync(record, PrimaryKeyName, cancellationToken);
+ return store.SaveAsync(record, cancellationToken);
}
///
public async Task FindAsync(KeyValueFilter filter, CancellationToken cancellationToken)
{
- var record = await _store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var record = await store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
return record == null ? null : Map(record);
}
///
public async Task> FindManyAsync(KeyValueFilter filter, CancellationToken cancellationToken)
{
- var records = await _store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
+ var records = await store.FindManyAsync(q => ApplyFilter(q, filter), cancellationToken);
return records.Select(Map);
}
///
public Task DeleteAsync(string key, CancellationToken cancellationToken)
{
- return _store.DeleteAsync(query => query.Is(nameof(KeyValuePairRecord.Key), key), cancellationToken);
+ return store.DeleteAsync(query => query.Is(nameof(KeyValuePairRecord.Id), key), cancellationToken);
}
private void ApplyFilter(ParameterizedQuery query, KeyValueFilter filter)
{
query
- .Is(nameof(KeyValuePairRecord.Key), filter.Key)
- .In(nameof(KeyValuePairRecord.Key), filter.Keys)
- .StartsWith(nameof(KeyValuePairRecord.Key), filter.StartsWith, filter.Key);
+ .Is(nameof(KeyValuePairRecord.Id), filter.Key)
+ .In(nameof(KeyValuePairRecord.Id), filter.Keys)
+ .StartsWith(nameof(KeyValuePairRecord.Id), filter.StartsWith, filter.Key);
}
- private KeyValuePairRecord Map(SerializedKeyValuePair kvp)
+ private KeyValuePairRecord Map(SerializedKeyValuePair source)
{
return new()
{
- Key = kvp.Key,
- Value = kvp.SerializedValue
+ Id = source.Id,
+ Value = source.SerializedValue,
+ TenantId = source.TenantId
};
}
- private SerializedKeyValuePair Map(KeyValuePairRecord kvp)
+ private SerializedKeyValuePair Map(KeyValuePairRecord source)
{
return new()
{
- Key = kvp.Key,
- SerializedValue = kvp.Value
+ Id = source.Id,
+ SerializedValue = source.Value,
+ TenantId = source.TenantId
};
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Records/Record.cs b/src/modules/Elsa.Dapper/Records/Record.cs
new file mode 100644
index 0000000000..00fd5355c9
--- /dev/null
+++ b/src/modules/Elsa.Dapper/Records/Record.cs
@@ -0,0 +1,7 @@
+namespace Elsa.Dapper.Records;
+
+public class Record
+{
+ public string Id { get; set; } = default!;
+ public string? TenantId { get; set; }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Services/Store.cs b/src/modules/Elsa.Dapper/Services/Store.cs
index 9a4efd114d..64fad2aa2b 100644
--- a/src/modules/Elsa.Dapper/Services/Store.cs
+++ b/src/modules/Elsa.Dapper/Services/Store.cs
@@ -1,9 +1,12 @@
using Dapper;
+using Elsa.Common.Contracts;
using Elsa.Common.Entities;
using Elsa.Common.Models;
using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
+using Elsa.Dapper.Records;
+using Elsa.Tenants;
using JetBrains.Annotations;
namespace Elsa.Dapper.Services;
@@ -12,29 +15,18 @@ namespace Elsa.Dapper.Services;
/// Provides a generic store using Dapper.
///
[PublicAPI]
-public class Store where T : notnull
+public class Store(IDbConnectionProvider dbConnectionProvider, ITenantResolver tenantResolver, string tableName, string primaryKey = "Id")
+ where T : notnull
{
- private readonly IDbConnectionProvider _dbConnectionProvider;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public Store(IDbConnectionProvider dbConnectionProvider, string tableName, string primaryKey = "Id")
- {
- _dbConnectionProvider = dbConnectionProvider;
- TableName = tableName;
- PrimaryKey = primaryKey;
- }
-
///
/// The name of the table.
///
- public string TableName { get; }
+ public string TableName { get; } = tableName;
///
/// The name of the primary key column.
///
- public string PrimaryKey { get; }
+ public string PrimaryKey { get; } = primaryKey;
///
/// Finds a single record.
@@ -44,10 +36,19 @@ public Store(IDbConnectionProvider dbConnectionProvider, string tableName, strin
/// The record, if found.
public async Task FindAsync(Action filter, CancellationToken cancellationToken = default)
{
- var query = _dbConnectionProvider.CreateQuery().From(TableName);
- filter(query);
- using var connection = _dbConnectionProvider.GetConnection();
- return await query.FirstOrDefaultAsync(connection);
+ return await FindAsync(filter, false, cancellationToken);
+ }
+
+ ///
+ /// Finds a single record.
+ ///
+ /// The conditions to apply to the query.
+ /// Whether to ignore the tenant filter.
+ /// The cancellation token.
+ /// The record, if found.
+ public async Task FindAsync(Action filter, bool tenantAgnostic = false, CancellationToken cancellationToken = default)
+ {
+ return await FindAsync(filter, null, null, tenantAgnostic, cancellationToken);
}
///
@@ -60,10 +61,28 @@ public Store(IDbConnectionProvider dbConnectionProvider, string tableName, strin
/// The record, if found.
public async Task FindAsync(Action filter, string orderKey, OrderDirection orderDirection, CancellationToken cancellationToken = default)
{
- using var connection = _dbConnectionProvider.GetConnection();
- var query = _dbConnectionProvider.CreateQuery().From(TableName);
+ return await FindAsync(filter, orderKey, orderDirection, false, cancellationToken);
+ }
+
+ ///
+ /// Finds a single record using the specified order key selector and order direction.
+ ///
+ /// The conditions to apply to the query.
+ /// The order key.
+ /// The order direction.
+ /// Whether to ignore the tenant filter.
+ /// The cancellation token.
+ /// The record, if found.
+ public async Task FindAsync(Action filter, string? orderKey = null, OrderDirection? orderDirection = null, bool tenantAgnostic = false, CancellationToken cancellationToken = default)
+ {
+ var query = dbConnectionProvider.CreateQuery().From(TableName);
+ await ApplyTenantFilterAsync(query, tenantAgnostic, cancellationToken);
filter(query);
- query = query.OrderBy(orderKey, orderDirection);
+
+ if (orderKey != null && orderDirection != null)
+ query = query.OrderBy(orderKey, orderDirection.Value);
+
+ using var connection = dbConnectionProvider.GetConnection();
return await query.FirstOrDefaultAsync(connection);
}
@@ -76,8 +95,25 @@ public Store(IDbConnectionProvider dbConnectionProvider, string tableName, strin
/// The order direction.
/// The cancellation token.
/// A page of records.
- public async Task> FindManyAsync(Action filter, PageArgs pageArgs, string orderKey, OrderDirection orderDirection, CancellationToken cancellationToken = default) =>
- await FindManyAsync(filter, pageArgs, orderKey, orderDirection, cancellationToken);
+ public async Task> FindManyAsync(Action filter, PageArgs pageArgs, string orderKey, OrderDirection orderDirection, CancellationToken cancellationToken = default)
+ {
+ return await FindManyAsync(filter, pageArgs, orderKey, orderDirection, false, cancellationToken);
+ }
+
+ ///
+ /// Returns a page of records.
+ ///
+ /// The conditions to apply to the query.
+ /// The page arguments.
+ /// The order key selector.
+ /// The order direction.
+ /// Whether to ignore the tenant filter.
+ /// The cancellation token.
+ /// A page of records.
+ public async Task> FindManyAsync(Action filter, PageArgs pageArgs, string orderKey, OrderDirection orderDirection, bool tenantAgnostic, CancellationToken cancellationToken = default)
+ {
+ return await FindManyAsync(filter, pageArgs, orderKey, orderDirection, tenantAgnostic, cancellationToken);
+ }
///
/// Returns a page of records in the specified shape.
@@ -89,7 +125,21 @@ public Store(IDbConnectionProvider dbConnectionProvider, string tableName, strin
/// A page of records.
public async Task> FindManyAsync(Action filter, PageArgs pageArgs, IEnumerable orderFields, CancellationToken cancellationToken = default)
{
- return await FindManyAsync(filter, pageArgs, orderFields, cancellationToken);
+ return await FindManyAsync(filter, pageArgs, orderFields, false, cancellationToken);
+ }
+
+ ///
+ /// Returns a page of records in the specified shape.
+ ///
+ /// The conditions to apply to the query.
+ /// The page arguments.
+ /// The fields by which to order the results.
+ /// Whether to ignore the tenant filter.
+ /// The cancellation token.
+ /// A page of records.
+ public async Task> FindManyAsync(Action filter, PageArgs pageArgs, IEnumerable orderFields, bool tenantAgnostic, CancellationToken cancellationToken = default)
+ {
+ return await FindManyAsync(filter, pageArgs, orderFields, tenantAgnostic, cancellationToken);
}
///
@@ -103,11 +153,27 @@ public async Task> FindManyAsync(Action filter, Page
/// The shape type.
/// A page of records.
public async Task> FindManyAsync(Action filter, PageArgs pageArgs, string orderKey, OrderDirection orderDirection, CancellationToken cancellationToken = default)
+ {
+ return await FindManyAsync(filter, pageArgs, orderKey, orderDirection, false, cancellationToken);
+ }
+
+ ///
+ /// Returns a page of records in the specified shape.
+ ///
+ /// The conditions to apply to the query.
+ /// The page arguments.
+ /// The order key selector.
+ /// The order direction.
+ /// Whether to ignore the tenant filter.
+ /// The cancellation token.
+ /// The shape type.
+ /// A page of records.
+ public async Task> FindManyAsync