From 83c0b48c0d473a4949cd9887817f927b8d9ea116 Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 21 Dec 2017 16:32:44 +0100 Subject: [PATCH 001/462] Prima bozza --- WorkflowCore.sln | 9 +- .../InitDB.sql | 35 +++++ .../ServiceCollectionExtensions.cs | 20 +++ .../Services/SqlServerQueueProvider.cs | 100 +++++++++++++ .../SqlServerQueueProviderMigrator.cs | 141 ++++++++++++++++++ ...rkflowCore.QueueProviders.SqlServer.csproj | 21 +++ .../SqlServerQueueProviderFixture.cs | 36 +++++ .../WorkflowCore.Tests.SqlServer.csproj | 1 + 8 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/InitDB.sql create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj create mode 100644 test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 06e23da0f..70c751184 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27004.2008 +VisualStudioVersion = 15.0.27004.2010 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF47161E-E399-451C-BDE8-E92AAD3BD761}" EndProject @@ -106,6 +106,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample16", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchPad\ScratchPad.csproj", "{6396453F-4D0E-4CD4-BC89-87E8970F2A80}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.QueueProviders.SqlServer", "src\providers\WorkflowCore.QueueProviders.SqlServer\WorkflowCore.QueueProviders.SqlServer.csproj", "{7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -268,6 +270,10 @@ Global {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Debug|Any CPU.Build.0 = Debug|Any CPU {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Release|Any CPU.ActiveCfg = Release|Any CPU {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Release|Any CPU.Build.0 = Release|Any CPU + {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -315,6 +321,7 @@ Global {9B7811AC-68D6-4D19-B1E9-65423393ED83} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {0C9617A9-C8B7-45F6-A54A-261A23AC881B} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {6396453F-4D0E-4CD4-BC89-87E8970F2A80} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} + {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/InitDB.sql b/src/providers/WorkflowCore.QueueProviders.SqlServer/InitDB.sql new file mode 100644 index 000000000..b8320d039 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/InitDB.sql @@ -0,0 +1,35 @@ + + +-- Verifica attivazione SSSB --------------------------------------------------------- +-- Per disattivare ALTER DATABASE MOSE SET DISABLE_BROKER; +-- Per generare nuovo id alter database MOSE set NEW_BROKER + +declare @b bit + +select @b=is_broker_enabled +from sys.databases +where name='MOSE' + +if @b=1 +begin + print 'SSSB già attivo' +end +else +begin + print 'Attivazione SSSB' + ALTER DATABASE MOSE SET ENABLE_BROKER + WITH ROLLBACK IMMEDIATE; + print 'SSSB adesso è attivo' +end + +GO + + + +CREATE MESSAGE TYPE +[//workflow-core/{instance}/workflow] +VALIDATION = WELL_FORMED_XML; + +CREATE MESSAGE TYPE +[//workflow-core/{instance}/event] +VALIDATION = WELL_FORMED_XML; \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..be0f9712d --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using WorkflowCore.Models; + +using WorkflowCore.QueueProviders.SqlServer.Services; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, string workflowHostName, bool canCreateDB) + { + options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canCreateDB)); + return options; + } + } +} diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs new file mode 100644 index 000000000..767f41488 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -0,0 +1,100 @@ +using System; +using System.Data.SqlClient; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using WorkflowCore.Interface; + +namespace WorkflowCore.QueueProviders.SqlServer.Services { + public class SqlServerQueueProvider : IQueueProvider + { + readonly string _connectionString; + readonly string _workflowHostName; + readonly bool _canMigrateDb; + SqlConnection _cn; + + public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb) { + _connectionString = connectionString; + _workflowHostName = workflowHostName; + _canMigrateDb = canMigrateDb; + + IsDequeueBlocking = true; + + } + + public void Dispose() { + Stop().Wait(); + } + + public Task QueueWork(string id, QueueType queue) { + throw new NotImplementedException(); + + /* + DECLARE @InitDlgHandle UNIQUEIDENTIFIER + DECLARE @RequestMessage VARCHAR(1000) + BEGIN TRAN + + --Determine the Initiator Service, Target Service and the Contract + BEGIN DIALOG @InitDlgHandle + FROM SERVICE + [//workflow-core/UnitTest/initiatorService] + TO SERVICE + '//workflow-core/UnitTest/targetService' + ON CONTRACT + [//workflow-core/UnitTest/contract] + WITH ENCRYPTION=OFF; + + --Prepare the Message + SELECT @RequestMessage = N' Send a Message to Target '; + + --Send the Message + SEND ON CONVERSATION @InitDlgHandle + MESSAGE TYPE + [//workflow-core/UnitTest/workflow] + (@RequestMessage); + + SELECT @RequestMessage AS SentRequestMessage; + COMMIT TRAN + + */ + } + + public Task DequeueWork(QueueType queue, CancellationToken cancellationToken) { + throw new NotImplementedException(); + + + /* + DECLARE @TargetDlgHandle UNIQUEIDENTIFIER + DECLARE @ReplyMessage VARCHAR(1000) + DECLARE @ReplyMessageName Sysname + BEGIN TRAN; + --Receive message from Initiator + RECEIVE TOP(1) + @TargetDlgHandle=Conversation_Handle + ,@ReplyMessage=Message_Body + ,@ReplyMessageName=Message_Type_Name + FROM workflowcore_UnitTest; + + SELECT @TargetDlgHandle, @ReplyMessage,@ReplyMessageName + COMMIT TRAN + */ + } + + public bool IsDequeueBlocking { get; } + + public Task Start() { + _cn = new SqlConnection(_connectionString); + if (_canMigrateDb) { + var mig = new SqlServerQueueProviderMigrator(_connectionString, _workflowHostName); + mig.MigrateDb(); + } + return Task.CompletedTask; + } + + public Task Stop() { + _cn.Close(); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs new file mode 100644 index 000000000..6b48fcc5b --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -0,0 +1,141 @@ +#region using + +using System; +using System.Data.SqlClient; +using System.Linq; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer.Services { + public class SqlServerQueueProviderMigrator { + readonly string _connectionString; + + readonly string _workflowMessageType; + readonly string _eventMessageType; + readonly string _contractName; + readonly string _queueName; + readonly string _initiatorServiceName; + readonly string _targetServiceName; + + public SqlServerQueueProviderMigrator(string connectionString, string workflowHostName) { + _connectionString = connectionString; + + _workflowMessageType = $"//workflow-core/{workflowHostName}/workflow"; + _eventMessageType = $"//workflow-core/{workflowHostName}/event"; + _contractName = $"//workflow-core/{workflowHostName}/contract"; + _initiatorServiceName = $"//workflow-core/{workflowHostName}/initiatorService"; + _targetServiceName = $"//workflow-core/{workflowHostName}/targetService"; + _queueName = $"workflowcore_{workflowHostName}"; + } + + internal void MigrateDb() { + var cn = new SqlConnection(_connectionString); + try { + cn.Open(); + var tx = cn.BeginTransaction(); + + EnableBroker(cn,tx); + CreateMessageType(cn, tx, _workflowMessageType); + CreateMessageType(cn, tx, _eventMessageType); + CreateContract(cn, tx); + CreateQueue(cn, tx); + CreateService(cn, tx, _initiatorServiceName); + CreateService(cn, tx, _targetServiceName); + + tx.Commit(); + } + finally { + cn.Close(); + } + } + + void CreateService(SqlConnection cn, SqlTransaction tx, string serviceName) { + var cmdtext = @"select name from sys.services where name=@name"; + using (var cmd = CreateCommand(cn, tx, cmdtext, serviceName)) + { + var n = (string)cmd.ExecuteScalar(); + + if (!String.IsNullOrEmpty(n)) return; + } + + cmdtext = $"CREATE SERVICE [{serviceName}] ON QUEUE {_queueName};"; + using (var cmd = CreateCommand(cn, tx, cmdtext)) + { + cmd.ExecuteNonQuery(); + } + } + + void CreateQueue(SqlConnection cn, SqlTransaction tx) { + var cmdtext = @"select name from sys.service_queues where name=@name"; + using (var cmd = CreateCommand(cn, tx, cmdtext, _queueName)) + { + var n = (string)cmd.ExecuteScalar(); + + if (!String.IsNullOrEmpty(n)) return; + } + + cmdtext = $"CREATE QUEUE {_queueName};"; + using (var cmd = CreateCommand(cn, tx, cmdtext)) + { + cmd.ExecuteNonQuery(); + } + + } + + void CreateContract(SqlConnection cn, SqlTransaction tx) { + + var cmdtext = @"select name from sys.service_contracts where name=@name"; + using (var cmd = CreateCommand(cn, tx, cmdtext, _contractName)) + { + var n = (string)cmd.ExecuteScalar(); + + if (!String.IsNullOrEmpty(n)) return; + } + + cmdtext = $"CREATE CONTRACT [{_contractName}] ( [{_workflowMessageType}] SENT BY INITIATOR, [{_eventMessageType}] SENT BY INITIATOR);"; + using (var cmd = CreateCommand(cn, tx, cmdtext)) + { + cmd.ExecuteNonQuery(); + } + } + + void CreateMessageType(SqlConnection cn, SqlTransaction tx, string message) { + + var cmdtext = @"select name from sys.service_message_types where name=@name"; + using (var cmd = CreateCommand(cn, tx, cmdtext, message)) { + var n = (string)cmd.ExecuteScalar(); + + if (!String.IsNullOrEmpty(n)) return; + } + + cmdtext = $"CREATE MESSAGE TYPE [{message}] VALIDATION = WELL_FORMED_XML;"; + using (var cmd = CreateCommand(cn, tx, cmdtext)) + { + cmd.ExecuteNonQuery(); + } + } + + void EnableBroker(SqlConnection cn, SqlTransaction tx) { + + var cmdtext = @"select is_broker_enabled from sys.databases where name = @name"; + using (var cmd = CreateCommand(cn, tx, cmdtext, cn.Database)) + { + bool isBrokerEnabled = (bool)cmd.ExecuteScalar(); + if (isBrokerEnabled) return; + } + + var msg = $"Service Broker not enabled on database {cn.Database}. Execute 'ALTER DATABASE {cn.Database} SET ENABLE_BROKER' in single user mode "; + throw new InvalidOperationException(msg); + } + + static SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name=null) { + var cmd = cn.CreateCommand(); + cmd.Transaction = tx; + cmd.CommandText = cmdtext; + if (name != null) { + cmd.Parameters.AddWithValue("name", name); + } + return cmd; + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj new file mode 100644 index 000000000..a37ffaa9f --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + + + + + + + + + + + + + ..\..\..\..\..\Users\r.paterlini\.nuget\packages\system.data.sqlclient\4.3.1\ref\netstandard1.3\System.Data.SqlClient.dll + + + + diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs new file mode 100644 index 000000000..cbd4b5abc --- /dev/null +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq; + +using WorkflowCore.Interface; +using WorkflowCore.QueueProviders.SqlServer.Services; + +using Xunit; + +namespace WorkflowCore.Tests.SqlServer { + + //[Collection("SqlServer collection")] + public class SqlServerQueueProviderFixture : IDisposable + { + readonly SqlServerQueueProvider _qb; + + public SqlServerQueueProviderFixture(/*SqlDockerSetup setup*/) { + var connectionString = "Server=(local);Database=wfc;User Id=wfc;Password=wfc;"; //SqlDockerSetup.ConnectionString; + + _qb = new SqlServerQueueProvider(connectionString, "UnitTest", true); + _qb.Start().Wait(); + } + + public void Dispose() + { + _qb.Dispose(); + } + + [Fact] + public void Test() + { + _qb.QueueWork("1", QueueType.Event); + } + + + } +} \ No newline at end of file diff --git a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj index d81b3ffa0..6c642fa8c 100644 --- a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj +++ b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj @@ -12,6 +12,7 @@ + From 8daa5b774ea026727c03b78a8ddcf6920b3f08be Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Tue, 2 Jan 2018 14:15:57 +0100 Subject: [PATCH 002/462] First working release. --- WorkflowCore.sln | 16 +- WorkflowCore.sln.DotSettings | 18 ++ .../README.md | 35 ++++ .../ServiceCollectionExtensions.cs | 15 +- .../Services/SqlConnectionHelper.cs | 26 +++ .../Services/SqlServerNames.cs | 50 +++++ .../Services/SqlServerQueueProvider.cs | 188 ++++++++++++------ .../SqlServerQueueProviderMigrator.cs | 115 ++++++----- .../HelloWorldWorkflow.cs | 27 +++ .../WorkflowCore.SampleSqlServer/Program.cs | 63 ++++++ .../WorkflowCore.SampleSqlServer/README.md | 25 +++ .../Steps/GoodbyeWorld.cs | 31 +++ .../Steps/HelloWorld.cs | 22 ++ .../WorkflowCore.SampleSqlServer.csproj | 32 +++ .../SqlServerQueueProviderFixture.cs | 32 ++- 15 files changed, 552 insertions(+), 143 deletions(-) create mode 100644 WorkflowCore.sln.DotSettings create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/README.md create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs create mode 100644 src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs create mode 100644 src/samples/WorkflowCore.SampleSqlServer/Program.cs create mode 100644 src/samples/WorkflowCore.SampleSqlServer/README.md create mode 100644 src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs create mode 100644 src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs create mode 100644 src/samples/WorkflowCore.SampleSqlServer/WorkflowCore.SampleSqlServer.csproj diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 84e56453c..56a7a175e 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -105,11 +105,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample15", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample16", "src\samples\WorkflowCore.Sample16\WorkflowCore.Sample16.csproj", "{0C9617A9-C8B7-45F6-A54A-261A23AC881B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchPad\ScratchPad.csproj", "{6396453F-4D0E-4CD4-BC89-87E8970F2A80}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample17", "src\samples\WorkflowCore.Sample17\WorkflowCore.Sample17.csproj", "{42F475BC-95F4-42E1-8CCD-7B9C27487E33}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.QueueProviders.SqlServer", "src\providers\WorkflowCore.QueueProviders.SqlServer\WorkflowCore.QueueProviders.SqlServer.csproj", "{7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.QueueProviders.SqlServer", "src\providers\WorkflowCore.QueueProviders.SqlServer\WorkflowCore.QueueProviders.SqlServer.csproj", "{7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.SampleSqlServer", "src\samples\WorkflowCore.SampleSqlServer\WorkflowCore.SampleSqlServer.csproj", "{4DA898F5-0299-48DA-B073-F0A4BB253B9C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -269,10 +269,6 @@ Global {0C9617A9-C8B7-45F6-A54A-261A23AC881B}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C9617A9-C8B7-45F6-A54A-261A23AC881B}.Release|Any CPU.ActiveCfg = Release|Any CPU {0C9617A9-C8B7-45F6-A54A-261A23AC881B}.Release|Any CPU.Build.0 = Release|Any CPU - {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Release|Any CPU.Build.0 = Release|Any CPU {42F475BC-95F4-42E1-8CCD-7B9C27487E33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {42F475BC-95F4-42E1-8CCD-7B9C27487E33}.Debug|Any CPU.Build.0 = Debug|Any CPU {42F475BC-95F4-42E1-8CCD-7B9C27487E33}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -281,6 +277,10 @@ Global {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Debug|Any CPU.Build.0 = Debug|Any CPU {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Release|Any CPU.ActiveCfg = Release|Any CPU {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Release|Any CPU.Build.0 = Release|Any CPU + {4DA898F5-0299-48DA-B073-F0A4BB253B9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DA898F5-0299-48DA-B073-F0A4BB253B9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DA898F5-0299-48DA-B073-F0A4BB253B9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DA898F5-0299-48DA-B073-F0A4BB253B9C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -327,9 +327,9 @@ Global {EC497168-5347-4E70-9D9E-9C2F826C1CDF} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {9B7811AC-68D6-4D19-B1E9-65423393ED83} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {0C9617A9-C8B7-45F6-A54A-261A23AC881B} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} - {6396453F-4D0E-4CD4-BC89-87E8970F2A80} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {42F475BC-95F4-42E1-8CCD-7B9C27487E33} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} + {4DA898F5-0299-48DA-B073-F0A4BB253B9C} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/WorkflowCore.sln.DotSettings b/WorkflowCore.sln.DotSettings new file mode 100644 index 000000000..f7baff92b --- /dev/null +++ b/WorkflowCore.sln.DotSettings @@ -0,0 +1,18 @@ + + NEXT_LINE + NEXT_LINE + NEXT_LINE + + NEXT_LINE + NEVER + NEXT_LINE + System + System.Linq + True + True + True + True + True + True + True + True \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md new file mode 100644 index 000000000..4f0b2bdf7 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md @@ -0,0 +1,35 @@ +# SQL Server Service Broker queue provider for Workflow Core + +Provides distributed worker support on [Workflow Core](../../README.md) using [SQL Server Service Broker](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-service-broker). + +This makes it possible to have a cluster of nodes processing your workflows, along with a distributed lock manager. + +## Installing + +Install the NuGet package "WorkflowCore.QueueProviders.SqlServer" + +``` +PM> Install-Package WorkflowCore.QueueProviders.SqlServer -Pre +``` + +## Usage + +Use the .UseSqlServerQueue extension method when building your service provider. + +```C# +services.AddWorkflow(x => x.UseSqlServerQueue(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canCreateDB)); + +``` + +## Running test + +It require a SQL Server 2016 database available with this connection string: + + "Server=(local);Database=wfc;User Id=wfc;Password=wfc;" + +and SQL Server Service Broker enabled. + +```sql + ALTER DATABASE wcf SET ENABLE_BROKER + WITH ROLLBACK IMMEDIATE; +``` diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index be0f9712d..2a024c847 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -1,20 +1,23 @@ -using System; -using System.Collections.Generic; +#region using + +using System; using System.Linq; -using System.Threading.Tasks; -using WorkflowCore.Models; +using Microsoft.Extensions.Logging; +using WorkflowCore.Models; using WorkflowCore.QueueProviders.SqlServer.Services; +#endregion + namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, string workflowHostName, bool canCreateDB) { - options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canCreateDB)); + options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canCreateDB/*, sp.GetService()*/)); return options; } } -} +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs new file mode 100644 index 000000000..f71b28a10 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs @@ -0,0 +1,26 @@ +#region using + +using System; +using System.Data.SqlClient; +using System.Linq; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer.Services +{ + public class SqlConnectionHelper + { + internal static SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name = null) + { + var cmd = cn.CreateCommand(); + cmd.Transaction = tx; + cmd.CommandText = cmdtext; + if (name != null) + { + cmd.Parameters.AddWithValue("name", name); + } + + return cmd; + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs new file mode 100644 index 000000000..29df73478 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs @@ -0,0 +1,50 @@ +#region using + +using System; +using System.Linq; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer.Services +{ + public class SqlServerNames + { + public SqlServerNames(string workflowHostName) + { + WorkflowMessageType = $"//workflow-core/{workflowHostName}/workflow"; + EventMessageType = $"//workflow-core/{workflowHostName}/event"; + + EventContractName = $"//workflow-core/{workflowHostName}/eventContract"; + WorkflowContractName = $"//workflow-core/{workflowHostName}/workflowContract"; + + InitiatorEventServiceName = $"//workflow-core/{workflowHostName}/initiatorEventService"; + TargetEventServiceName = $"//workflow-core/{workflowHostName}/targetEventService"; + + InitiatorWorkflowServiceName = $"//workflow-core/{workflowHostName}/initiatorWorkflowService"; + TargetWorkflowServiceName = $"//workflow-core/{workflowHostName}/targetWorkflowService"; + + EventQueueName = $"//workflow-core/{workflowHostName}/eventQueue"; + WorkflowQueueName = $"//workflow-core/{workflowHostName}/workflowQueue"; + } + + public string WorkflowContractName { get; } + + public string TargetEventServiceName { get; } + + public string InitiatorEventServiceName { get; } + + public string WorkflowQueueName { get; } + + public string EventQueueName { get; } + + public string TargetWorkflowServiceName { get; } + + public string InitiatorWorkflowServiceName { get; } + + public string EventContractName { get; } + + public string EventMessageType { get; } + + public string WorkflowMessageType { get; } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index 767f41488..615ad431a 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -1,100 +1,156 @@ -using System; +#region using + +using System; using System.Data.SqlClient; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + using WorkflowCore.Interface; -namespace WorkflowCore.QueueProviders.SqlServer.Services { +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer.Services +{ public class SqlServerQueueProvider : IQueueProvider { readonly string _connectionString; readonly string _workflowHostName; readonly bool _canMigrateDb; SqlConnection _cn; + readonly SqlServerNames _names; + //private ILogger _lg; + + public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb/*, ILoggerFactory logFactory*/) + { + //_lg = logFactory.CreateLogger(); - public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb) { _connectionString = connectionString; _workflowHostName = workflowHostName; _canMigrateDb = canMigrateDb; + _names = new SqlServerNames(workflowHostName); IsDequeueBlocking = true; - } - public void Dispose() { - Stop().Wait(); + public bool IsDequeueBlocking { get; } + + public async Task Start() + { + _cn = new SqlConnection(_connectionString); + + if (_canMigrateDb) + { + var mig = new SqlServerQueueProviderMigrator(_connectionString, _workflowHostName); + mig.MigrateDb(); + } } - public Task QueueWork(string id, QueueType queue) { - throw new NotImplementedException(); - - /* - DECLARE @InitDlgHandle UNIQUEIDENTIFIER - DECLARE @RequestMessage VARCHAR(1000) - BEGIN TRAN - - --Determine the Initiator Service, Target Service and the Contract - BEGIN DIALOG @InitDlgHandle - FROM SERVICE - [//workflow-core/UnitTest/initiatorService] - TO SERVICE - '//workflow-core/UnitTest/targetService' - ON CONTRACT - [//workflow-core/UnitTest/contract] - WITH ENCRYPTION=OFF; - - --Prepare the Message - SELECT @RequestMessage = N' Send a Message to Target '; - - --Send the Message - SEND ON CONVERSATION @InitDlgHandle - MESSAGE TYPE - [//workflow-core/UnitTest/workflow] - (@RequestMessage); - - SELECT @RequestMessage AS SentRequestMessage; - COMMIT TRAN - - */ + public async Task Stop() + { + _cn.Close(); } - public Task DequeueWork(QueueType queue, CancellationToken cancellationToken) { - throw new NotImplementedException(); - - - /* - DECLARE @TargetDlgHandle UNIQUEIDENTIFIER - DECLARE @ReplyMessage VARCHAR(1000) - DECLARE @ReplyMessageName Sysname - BEGIN TRAN; - --Receive message from Initiator - RECEIVE TOP(1) - @TargetDlgHandle=Conversation_Handle - ,@ReplyMessage=Message_Body - ,@ReplyMessageName=Message_Type_Name - FROM workflowcore_UnitTest; - - SELECT @TargetDlgHandle, @ReplyMessage,@ReplyMessageName - COMMIT TRAN - */ + public void Dispose() + { + Stop().Wait(); } - public bool IsDequeueBlocking { get; } - public Task Start() { - _cn = new SqlConnection(_connectionString); - if (_canMigrateDb) { - var mig = new SqlServerQueueProviderMigrator(_connectionString, _workflowHostName); - mig.MigrateDb(); + public async Task QueueWork(string id, QueueType queue) + { + var cn = new SqlConnection(_connectionString); + try + { + string msgType, initiatorService, targetService, contractName; + if (queue == QueueType.Workflow) + { + msgType = _names.WorkflowMessageType; + initiatorService = _names.InitiatorWorkflowServiceName; + targetService = _names.TargetWorkflowServiceName; + contractName = _names.WorkflowContractName; + } else + { + msgType = _names.EventMessageType; + initiatorService = _names.InitiatorEventServiceName; + targetService = _names.TargetEventServiceName; + contractName = _names.EventContractName; + } + + var sql = $@" +DECLARE @InitDlgHandle UNIQUEIDENTIFIER +BEGIN TRAN + +--Determine the Initiator Service, Target Service and the Contract +BEGIN DIALOG @InitDlgHandle +FROM SERVICE +[{initiatorService}] +TO SERVICE +'{targetService}' +ON CONTRACT +[{contractName}] +WITH ENCRYPTION=OFF; + +--Send the Message +SEND ON CONVERSATION @InitDlgHandle +MESSAGE TYPE +[{msgType}] +(@RequestMessage); + +COMMIT TRAN +"; + + + cn.Open(); + using (var cmd = SqlConnectionHelper.CreateCommand(cn, null, sql)) + { + cmd.Parameters.AddWithValue("@RequestMessage", id); + await cmd.ExecuteNonQueryAsync(); + } + } finally + { + cn.Close(); } - return Task.CompletedTask; } - public Task Stop() { - _cn.Close(); - return Task.CompletedTask; + public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) + { + var cn = new SqlConnection(_connectionString); + try + { + var queueName = queue == QueueType.Workflow ? _names.WorkflowQueueName : _names.EventQueueName; + + var sql = $@" +DECLARE @TargetDlgHandle UNIQUEIDENTIFIER +DECLARE @Message varbinary(max) +DECLARE @MessageName Sysname + +BEGIN TRAN; + +WAITFOR ( + RECEIVE TOP(1) + @TargetDlgHandle=Conversation_Handle + ,@Message=Message_Body + ,@MessageName=Message_Type_Name + FROM [{queueName}]), +TIMEOUT 1000; + +SELECT cast(@Message as nvarchar(max)) +COMMIT TRAN +"; + + cn.Open(); + using (var cmd = SqlConnectionHelper.CreateCommand(cn, null, sql)) + { + var msg = await cmd.ExecuteScalarAsync(cancellationToken); + return msg is DBNull ? null : (string)msg; + } + } finally + { + cn.Close(); + } } } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index 6b48fcc5b..c5ddda6f0 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -6,136 +6,133 @@ #endregion -namespace WorkflowCore.QueueProviders.SqlServer.Services { - public class SqlServerQueueProviderMigrator { +namespace WorkflowCore.QueueProviders.SqlServer.Services +{ + public class SqlServerQueueProviderMigrator + { readonly string _connectionString; - readonly string _workflowMessageType; - readonly string _eventMessageType; - readonly string _contractName; - readonly string _queueName; - readonly string _initiatorServiceName; - readonly string _targetServiceName; + readonly SqlServerNames _names; - public SqlServerQueueProviderMigrator(string connectionString, string workflowHostName) { + public SqlServerQueueProviderMigrator(string connectionString, string workflowHostName) + { _connectionString = connectionString; - _workflowMessageType = $"//workflow-core/{workflowHostName}/workflow"; - _eventMessageType = $"//workflow-core/{workflowHostName}/event"; - _contractName = $"//workflow-core/{workflowHostName}/contract"; - _initiatorServiceName = $"//workflow-core/{workflowHostName}/initiatorService"; - _targetServiceName = $"//workflow-core/{workflowHostName}/targetService"; - _queueName = $"workflowcore_{workflowHostName}"; + _names = new SqlServerNames(workflowHostName); } - internal void MigrateDb() { + + internal void MigrateDb() + { var cn = new SqlConnection(_connectionString); - try { + try + { cn.Open(); var tx = cn.BeginTransaction(); - EnableBroker(cn,tx); - CreateMessageType(cn, tx, _workflowMessageType); - CreateMessageType(cn, tx, _eventMessageType); - CreateContract(cn, tx); - CreateQueue(cn, tx); - CreateService(cn, tx, _initiatorServiceName); - CreateService(cn, tx, _targetServiceName); + EnableBroker(cn, tx); + CreateMessageType(cn, tx, _names.WorkflowMessageType); + CreateMessageType(cn, tx, _names.EventMessageType); + + CreateContract(cn, tx, _names.EventContractName, _names.EventMessageType); + CreateContract(cn, tx, _names.WorkflowContractName, _names.WorkflowMessageType); + + CreateQueue(cn, tx, _names.EventQueueName); + CreateQueue(cn, tx, _names.WorkflowQueueName); + + CreateService(cn, tx, _names.InitiatorEventServiceName, _names.EventQueueName, _names.EventContractName); + CreateService(cn, tx, _names.TargetEventServiceName, _names.EventQueueName, _names.EventContractName); + + CreateService(cn, tx, _names.InitiatorWorkflowServiceName, _names.WorkflowQueueName, _names.WorkflowContractName); + CreateService(cn, tx, _names.TargetWorkflowServiceName, _names.WorkflowQueueName, _names.WorkflowContractName); tx.Commit(); - } - finally { + } finally + { cn.Close(); } } - void CreateService(SqlConnection cn, SqlTransaction tx, string serviceName) { + private static void CreateService(SqlConnection cn, SqlTransaction tx, string name, string queueName, string contractName) + { var cmdtext = @"select name from sys.services where name=@name"; - using (var cmd = CreateCommand(cn, tx, cmdtext, serviceName)) + using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, name)) { var n = (string)cmd.ExecuteScalar(); if (!String.IsNullOrEmpty(n)) return; } - cmdtext = $"CREATE SERVICE [{serviceName}] ON QUEUE {_queueName};"; - using (var cmd = CreateCommand(cn, tx, cmdtext)) + cmdtext = $"CREATE SERVICE [{name}] ON QUEUE [{queueName}]([{contractName}]);"; + using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext)) { cmd.ExecuteNonQuery(); } } - void CreateQueue(SqlConnection cn, SqlTransaction tx) { + private static void CreateQueue(SqlConnection cn, SqlTransaction tx, string queueName) + { var cmdtext = @"select name from sys.service_queues where name=@name"; - using (var cmd = CreateCommand(cn, tx, cmdtext, _queueName)) + using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, queueName)) { var n = (string)cmd.ExecuteScalar(); if (!String.IsNullOrEmpty(n)) return; } - cmdtext = $"CREATE QUEUE {_queueName};"; - using (var cmd = CreateCommand(cn, tx, cmdtext)) + cmdtext = $"CREATE QUEUE [{queueName}];"; + using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext)) { cmd.ExecuteNonQuery(); } - } - void CreateContract(SqlConnection cn, SqlTransaction tx) { - + private static void CreateContract(SqlConnection cn, SqlTransaction tx, string contractName, string messageName) + { var cmdtext = @"select name from sys.service_contracts where name=@name"; - using (var cmd = CreateCommand(cn, tx, cmdtext, _contractName)) + using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, contractName)) { var n = (string)cmd.ExecuteScalar(); if (!String.IsNullOrEmpty(n)) return; } - cmdtext = $"CREATE CONTRACT [{_contractName}] ( [{_workflowMessageType}] SENT BY INITIATOR, [{_eventMessageType}] SENT BY INITIATOR);"; - using (var cmd = CreateCommand(cn, tx, cmdtext)) + cmdtext = $"CREATE CONTRACT [{contractName}] ( [{messageName}] SENT BY INITIATOR);"; + using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext)) { cmd.ExecuteNonQuery(); } } - void CreateMessageType(SqlConnection cn, SqlTransaction tx, string message) { - + private static void CreateMessageType(SqlConnection cn, SqlTransaction tx, string message) + { var cmdtext = @"select name from sys.service_message_types where name=@name"; - using (var cmd = CreateCommand(cn, tx, cmdtext, message)) { + using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, message)) + { var n = (string)cmd.ExecuteScalar(); if (!String.IsNullOrEmpty(n)) return; } - cmdtext = $"CREATE MESSAGE TYPE [{message}] VALIDATION = WELL_FORMED_XML;"; - using (var cmd = CreateCommand(cn, tx, cmdtext)) + cmdtext = $"CREATE MESSAGE TYPE [{message}] VALIDATION = NONE;"; + using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext)) { cmd.ExecuteNonQuery(); } } - void EnableBroker(SqlConnection cn, SqlTransaction tx) { - + private static void EnableBroker(SqlConnection cn, SqlTransaction tx) + { var cmdtext = @"select is_broker_enabled from sys.databases where name = @name"; - using (var cmd = CreateCommand(cn, tx, cmdtext, cn.Database)) + using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, cn.Database)) { bool isBrokerEnabled = (bool)cmd.ExecuteScalar(); if (isBrokerEnabled) return; } - var msg = $"Service Broker not enabled on database {cn.Database}. Execute 'ALTER DATABASE {cn.Database} SET ENABLE_BROKER' in single user mode "; + var msg = + $"Service Broker not enabled on database {cn.Database}. Execute 'ALTER DATABASE {cn.Database} SET ENABLE_BROKER' in single user mode "; throw new InvalidOperationException(msg); } - - static SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name=null) { - var cmd = cn.CreateCommand(); - cmd.Transaction = tx; - cmd.CommandText = cmdtext; - if (name != null) { - cmd.Parameters.AddWithValue("name", name); - } - return cmd; - } } } \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs b/src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs new file mode 100644 index 000000000..0296b3265 --- /dev/null +++ b/src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs @@ -0,0 +1,27 @@ +#region using + +using System; +using System.Linq; + +using WorkflowCore.Interface; +using WorkflowCore.SampleSqlServer.Steps; + +#endregion + +namespace WorkflowCore.SampleSqlServer +{ + public class HelloWorldWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder builder) + { + var stepBuilder = builder + .StartWith() + .Delay(o => TimeSpan.FromSeconds(2)) + .Then(); + } + + public string Id => "HelloWorld"; + + public int Version => 1; + } +} \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/Program.cs b/src/samples/WorkflowCore.SampleSqlServer/Program.cs new file mode 100644 index 000000000..5a97fe9ce --- /dev/null +++ b/src/samples/WorkflowCore.SampleSqlServer/Program.cs @@ -0,0 +1,63 @@ +#region using + +using System; +using System.Linq; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +using WorkflowCore.Interface; +using WorkflowCore.SampleSqlServer.Steps; + +#endregion + +namespace WorkflowCore.SampleSqlServer +{ + class Program + { + private static readonly string _connectionString = "Server=(local);Database=wfc;User Id=wfc;Password=wfc;"; + private static ILogger _lg; + + public static void Main(string[] args) + { + IServiceProvider serviceProvider = ConfigureServices(); + + _lg = serviceProvider.GetService().CreateLogger(); + _lg.LogDebug("->"); + + //start the workflow host + var host = serviceProvider.GetService(); + host.RegisterWorkflow(); + host.Start(); + + host.StartWorkflow("HelloWorld", 1, null); + + Console.ReadLine(); + host.Stop(); + } + + private static IServiceProvider ConfigureServices() + { + //setup dependency injection + IServiceCollection services = new ServiceCollection(); + services.AddLogging(); + + services.AddWorkflow(x => + { + x.UseSqlServerQueue(_connectionString, "SampleSqlServer", true); + x.UseSqlServer(_connectionString, false, true); + x.UseSqlServerLocking(_connectionString); + } + ); + + services.AddTransient(); + + var serviceProvider = services.BuildServiceProvider(); + + //config logging + var loggerFactory = serviceProvider.GetService(); + loggerFactory.AddDebug(LogLevel.Debug); + return serviceProvider; + } + } +} \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/README.md b/src/samples/WorkflowCore.SampleSqlServer/README.md new file mode 100644 index 000000000..bbe3088a5 --- /dev/null +++ b/src/samples/WorkflowCore.SampleSqlServer/README.md @@ -0,0 +1,25 @@ +# SQL Server sample + +A sample to test SQL Server for persistenze, locking and queueing. + + +```c# +services.AddWorkflow(x => + { + x.UseSqlServerQueue(_connectionString, "SampleSqlServer", true); + x.UseSqlServer(_connectionString, false, true); + x.UseSqlServerLocking(_connectionString); + } + ); +``` + +It require a SQL Server database available with this connection string: + + "Server=(local);Database=wfc;User Id=wfc;Password=wfc;" + +and SQL Server Service Broker enabled. + +```sql + ALTER DATABASE wcf SET ENABLE_BROKER + WITH ROLLBACK IMMEDIATE; +``` \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs new file mode 100644 index 000000000..b7db914b0 --- /dev/null +++ b/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs @@ -0,0 +1,31 @@ +#region using + +using System; +using System.Linq; + +using Microsoft.Extensions.Logging; + +using WorkflowCore.Interface; +using WorkflowCore.Models; + +#endregion + +namespace WorkflowCore.SampleSqlServer.Steps +{ + public class GoodbyeWorld : StepBody + { + private readonly ILogger _logger; + + public GoodbyeWorld(ILoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Goodbye world"); + _logger.LogInformation("Hi there!"); + return ExecutionResult.Next(); + } + } +} \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs b/src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs new file mode 100644 index 000000000..5e50d2d59 --- /dev/null +++ b/src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs @@ -0,0 +1,22 @@ +#region using + +using System; +using System.Linq; + +using WorkflowCore.Interface; +using WorkflowCore.Models; + +#endregion + +namespace WorkflowCore.SampleSqlServer.Steps +{ + public class HelloWorld : StepBody + { + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Hello world"); + + return ExecutionResult.Next(); + } + } +} \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/WorkflowCore.SampleSqlServer.csproj b/src/samples/WorkflowCore.SampleSqlServer/WorkflowCore.SampleSqlServer.csproj new file mode 100644 index 000000000..3b1b6d98c --- /dev/null +++ b/src/samples/WorkflowCore.SampleSqlServer/WorkflowCore.SampleSqlServer.csproj @@ -0,0 +1,32 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + + + + + + + ..\..\..\..\..\Users\r.paterlini\.nuget\packages\microsoft.extensions.dependencyinjection\1.1.1\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll + + + ..\..\..\..\..\Users\r.paterlini\.nuget\packages\microsoft.extensions.dependencyinjection.abstractions\1.1.1\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + ..\..\..\..\..\Users\r.paterlini\.nuget\packages\microsoft.extensions.logging.abstractions\1.1.2\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll + + + + diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs index cbd4b5abc..8a318fc62 100644 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs @@ -1,23 +1,36 @@ -using System; +#region using + +using System; using System.Linq; +using System.Threading; + +using FluentAssertions; + +using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.QueueProviders.SqlServer.Services; using Xunit; -namespace WorkflowCore.Tests.SqlServer { +#endregion +namespace WorkflowCore.Tests.SqlServer +{ //[Collection("SqlServer collection")] public class SqlServerQueueProviderFixture : IDisposable { readonly SqlServerQueueProvider _qb; - public SqlServerQueueProviderFixture(/*SqlDockerSetup setup*/) { + public SqlServerQueueProviderFixture( /*SqlDockerSetup setup*/ ) + { var connectionString = "Server=(local);Database=wfc;User Id=wfc;Password=wfc;"; //SqlDockerSetup.ConnectionString; _qb = new SqlServerQueueProvider(connectionString, "UnitTest", true); _qb.Start().Wait(); + + while (_qb.DequeueWork(QueueType.Event, CancellationToken.None).Result != null) { } + while (_qb.DequeueWork(QueueType.Workflow, CancellationToken.None).Result != null) { } } public void Dispose() @@ -28,9 +41,20 @@ public void Dispose() [Fact] public void Test() { - _qb.QueueWork("1", QueueType.Event); + var id = Guid.NewGuid().ToString(); + + DoTest(id, QueueType.Event); + + id = Guid.NewGuid().ToString(); + DoTest(id, QueueType.Workflow); } + void DoTest(string id, QueueType queueType) + { + _qb.QueueWork(id, queueType).Wait(); + var res = _qb.DequeueWork(queueType, CancellationToken.None).Result; + res.Should().Be(id); + } } } \ No newline at end of file From 311ad9b5a59ccf196dd6c8ef6dfecbe6f3ae36cc Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 4 Jan 2018 09:51:26 +0100 Subject: [PATCH 003/462] Aggiunti test multithread --- .../README.md | 4 +- .../ServiceCollectionExtensions.cs | 15 ++- .../Services/SqlServerNames.cs | 36 +++++--- .../Services/SqlServerQueueProvider.cs | 45 +++++---- .../WorkflowCore.SampleSqlServer/Program.cs | 4 +- .../WorkflowCore.SampleSqlServer/README.md | 4 +- .../SqlServerQueueProviderFixture.cs | 92 +++++++++++++++++-- 7 files changed, 153 insertions(+), 47 deletions(-) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md index 4f0b2bdf7..08ffa95fc 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md @@ -1,6 +1,6 @@ # SQL Server Service Broker queue provider for Workflow Core -Provides distributed worker support on [Workflow Core](../../README.md) using [SQL Server Service Broker](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-service-broker). +Provides distributed worker support on [Workflow Core](../../../README.md) using [SQL Server Service Broker](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-service-broker). This makes it possible to have a cluster of nodes processing your workflows, along with a distributed lock manager. @@ -27,7 +27,7 @@ It require a SQL Server 2016 database available with this connection string: "Server=(local);Database=wfc;User Id=wfc;Password=wfc;" -and SQL Server Service Broker enabled. +and SQL Server Service Broker enabled (this command must be executed in single user mode). ```sql ALTER DATABASE wcf SET ENABLE_BROKER diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index 2a024c847..7c0924c22 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -3,8 +3,6 @@ using System; using System.Linq; -using Microsoft.Extensions.Logging; - using WorkflowCore.Models; using WorkflowCore.QueueProviders.SqlServer.Services; @@ -14,9 +12,18 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { - public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, string workflowHostName, bool canCreateDB) + public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, string workflowHostName, + bool canCreateDb = false) + { + options.UseQueueProvider(sp => + new SqlServerQueueProvider(connectionString, workflowHostName, canCreateDb /*, sp.GetService()*/)); + return options; + } + + public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, bool canCreateDb = false) { - options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canCreateDB/*, sp.GetService()*/)); + options.UseQueueProvider(sp => + new SqlServerQueueProvider(connectionString, "default", canCreateDb /*, sp.GetService()*/)); return options; } } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs index 29df73478..2b47b9d0a 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs @@ -7,15 +7,25 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { + /// + /// Build names for SSSB objects + /// + /// + /// Message type and contract are global, service name and queue different for every workflow host + /// public class SqlServerNames { + /// + /// ctor + /// + /// public SqlServerNames(string workflowHostName) { - WorkflowMessageType = $"//workflow-core/{workflowHostName}/workflow"; - EventMessageType = $"//workflow-core/{workflowHostName}/event"; + WorkflowMessageType = "//workflow-core/workflow"; + EventMessageType = "//workflow-core/event"; - EventContractName = $"//workflow-core/{workflowHostName}/eventContract"; - WorkflowContractName = $"//workflow-core/{workflowHostName}/workflowContract"; + EventContractName = "//workflow-core/eventContract"; + WorkflowContractName = "//workflow-core/workflowContract"; InitiatorEventServiceName = $"//workflow-core/{workflowHostName}/initiatorEventService"; TargetEventServiceName = $"//workflow-core/{workflowHostName}/targetEventService"; @@ -27,23 +37,23 @@ public SqlServerNames(string workflowHostName) WorkflowQueueName = $"//workflow-core/{workflowHostName}/workflowQueue"; } - public string WorkflowContractName { get; } + public string WorkflowContractName { get; } - public string TargetEventServiceName { get; } + public string TargetEventServiceName { get; } - public string InitiatorEventServiceName { get; } + public string InitiatorEventServiceName { get; } - public string WorkflowQueueName { get; } + public string WorkflowQueueName { get; } - public string EventQueueName { get; } + public string EventQueueName { get; } - public string TargetWorkflowServiceName { get; } + public string TargetWorkflowServiceName { get; } - public string InitiatorWorkflowServiceName { get; } + public string InitiatorWorkflowServiceName { get; } - public string EventContractName { get; } + public string EventContractName { get; } - public string EventMessageType { get; } + public string EventMessageType { get; } public string WorkflowMessageType { get; } } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index 615ad431a..fa555eb3f 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -6,8 +6,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; - using WorkflowCore.Interface; #endregion @@ -18,12 +16,13 @@ public class SqlServerQueueProvider : IQueueProvider { readonly string _connectionString; readonly string _workflowHostName; + readonly bool _canMigrateDb; - SqlConnection _cn; + readonly SqlServerNames _names; //private ILogger _lg; - public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb/*, ILoggerFactory logFactory*/) + public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb /*, ILoggerFactory logFactory*/) { //_lg = logFactory.CreateLogger(); @@ -39,8 +38,6 @@ public SqlServerQueueProvider(string connectionString, string workflowHostName, public async Task Start() { - _cn = new SqlConnection(_connectionString); - if (_canMigrateDb) { var mig = new SqlServerQueueProviderMigrator(_connectionString, _workflowHostName); @@ -50,7 +47,6 @@ public async Task Start() public async Task Stop() { - _cn.Close(); } public void Dispose() @@ -58,10 +54,18 @@ public void Dispose() Stop().Wait(); } - + /// + /// + /// Write a new id to the specified queue + /// + /// + /// + /// public async Task QueueWork(string id, QueueType queue) { - var cn = new SqlConnection(_connectionString); + if (String.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id), "Param id must not be null"); + + SqlConnection cn = null; try { string msgType, initiatorService, targetService, contractName; @@ -83,7 +87,6 @@ public async Task QueueWork(string id, QueueType queue) DECLARE @InitDlgHandle UNIQUEIDENTIFIER BEGIN TRAN ---Determine the Initiator Service, Target Service and the Contract BEGIN DIALOG @InitDlgHandle FROM SERVICE [{initiatorService}] @@ -93,16 +96,14 @@ ON CONTRACT [{contractName}] WITH ENCRYPTION=OFF; ---Send the Message SEND ON CONVERSATION @InitDlgHandle -MESSAGE TYPE -[{msgType}] +MESSAGE TYPE [{msgType}] (@RequestMessage); COMMIT TRAN "; - + cn = new SqlConnection(_connectionString); cn.Open(); using (var cmd = SqlConnectionHelper.CreateCommand(cn, null, sql)) { @@ -111,13 +112,20 @@ COMMIT TRAN } } finally { - cn.Close(); + cn?.Close(); } } + /// + /// + /// Get an id from the specified queue. + /// + /// + /// cancellationToken + /// Next id from queue, null if no message arrives in one second. public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) { - var cn = new SqlConnection(_connectionString); + SqlConnection cn = null; try { var queueName = queue == QueueType.Workflow ? _names.WorkflowQueueName : _names.EventQueueName; @@ -140,7 +148,8 @@ RECEIVE TOP(1) SELECT cast(@Message as nvarchar(max)) COMMIT TRAN "; - + + cn = new SqlConnection(_connectionString); cn.Open(); using (var cmd = SqlConnectionHelper.CreateCommand(cn, null, sql)) { @@ -149,7 +158,7 @@ COMMIT TRAN } } finally { - cn.Close(); + cn?.Close(); } } } diff --git a/src/samples/WorkflowCore.SampleSqlServer/Program.cs b/src/samples/WorkflowCore.SampleSqlServer/Program.cs index 5a97fe9ce..eebadc51c 100644 --- a/src/samples/WorkflowCore.SampleSqlServer/Program.cs +++ b/src/samples/WorkflowCore.SampleSqlServer/Program.cs @@ -38,15 +38,14 @@ public static void Main(string[] args) private static IServiceProvider ConfigureServices() { - //setup dependency injection IServiceCollection services = new ServiceCollection(); services.AddLogging(); services.AddWorkflow(x => { - x.UseSqlServerQueue(_connectionString, "SampleSqlServer", true); x.UseSqlServer(_connectionString, false, true); x.UseSqlServerLocking(_connectionString); + x.UseSqlServerQueue(_connectionString, true); } ); @@ -57,6 +56,7 @@ private static IServiceProvider ConfigureServices() //config logging var loggerFactory = serviceProvider.GetService(); loggerFactory.AddDebug(LogLevel.Debug); + return serviceProvider; } } diff --git a/src/samples/WorkflowCore.SampleSqlServer/README.md b/src/samples/WorkflowCore.SampleSqlServer/README.md index bbe3088a5..5d0cee5e6 100644 --- a/src/samples/WorkflowCore.SampleSqlServer/README.md +++ b/src/samples/WorkflowCore.SampleSqlServer/README.md @@ -17,9 +17,9 @@ It require a SQL Server database available with this connection string: "Server=(local);Database=wfc;User Id=wfc;Password=wfc;" -and SQL Server Service Broker enabled. +and SQL Server Service Broker enabled (this command must be executed in single user mode). ```sql ALTER DATABASE wcf SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE; -``` \ No newline at end of file +``` diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs index 8a318fc62..af138446c 100644 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs @@ -1,35 +1,43 @@ #region using using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; +using System.Threading.Tasks; using FluentAssertions; -using Microsoft.Extensions.Logging; - using WorkflowCore.Interface; using WorkflowCore.QueueProviders.SqlServer.Services; using Xunit; +using Xunit.Abstractions; #endregion namespace WorkflowCore.Tests.SqlServer { - //[Collection("SqlServer collection")] + [Collection("SqlServerBroker collection")] public class SqlServerQueueProviderFixture : IDisposable { + #region Init + readonly SqlServerQueueProvider _qb; + private readonly ITestOutputHelper _console; - public SqlServerQueueProviderFixture( /*SqlDockerSetup setup*/ ) + public SqlServerQueueProviderFixture(ITestOutputHelper output) { - var connectionString = "Server=(local);Database=wfc;User Id=wfc;Password=wfc;"; //SqlDockerSetup.ConnectionString; + _console = output; + var connectionString = "Server=(local);Database=wfc;User Id=wfc;Password=wfc;"; _qb = new SqlServerQueueProvider(connectionString, "UnitTest", true); _qb.Start().Wait(); while (_qb.DequeueWork(QueueType.Event, CancellationToken.None).Result != null) { } + while (_qb.DequeueWork(QueueType.Workflow, CancellationToken.None).Result != null) { } } @@ -38,8 +46,12 @@ public void Dispose() _qb.Dispose(); } + #endregion + + #region QueueDeque + [Fact] - public void Test() + public void QueueDequeTest() { var id = Guid.NewGuid().ToString(); @@ -56,5 +68,73 @@ void DoTest(string id, QueueType queueType) res.Should().Be(id); } + + #endregion + + [Fact] + public void MultiTest() + { + const int countEvent = 250; + const int countThread = 10; + const QueueType queueType = QueueType.Event; + + var guids = new ConcurrentDictionary(); + + + bool stop = false; + var sw = Stopwatch.StartNew(); + + _console.WriteLine("Start dequeue task"); + var thDeque = new List(); + for (int i = 0; i < countThread; i++) + { + Task t = Task.Factory.StartNew(() => + { + _console.WriteLine("-> Dequeue task " + Task.CurrentId); + while (!stop) + { + var id = _qb.DequeueWork(queueType, CancellationToken.None).Result; + if (id != null) guids.AddOrUpdate(id, 0, (key, oldval) => oldval + 1); + } + + _console.WriteLine("<- Dequeue task " + Task.CurrentId); + }); + thDeque.Add(t); + } + + _console.WriteLine("Start enqueue task"); + var thEnque = new List(); + for (int i = 0; i < countThread; i++) + { + Task t = Task.Factory.StartNew(() => + { + _console.WriteLine("-> Enqueue task " + Task.CurrentId); + + for (int j = 0; j < countEvent; j++) + { + var guid = Guid.NewGuid().ToString(); + guids.TryAdd(guid, 0); + _qb.QueueWork(guid, queueType).Wait(); + } + + _console.WriteLine("<- Enqueue task " + Task.CurrentId); + }); + thEnque.Add(t); + } + + Task.WaitAll(thEnque.ToArray()); + _console.WriteLine("Enqueue complete " + sw.ElapsedMilliseconds + " msec"); + + stop = true; + Task.WaitAll(thDeque.ToArray()); + _console.WriteLine("Dequeue complete " + sw.ElapsedMilliseconds + " msec"); + + foreach (var guid in guids) + { + guid.Value.Should().Be(1); + } + + _console.WriteLine("MultiTest complete " + (guids.Count/(sw.ElapsedMilliseconds/1000.0)) + " msg/sec"); + } } } \ No newline at end of file From 35f1ff3265be8aeee89d82b0e694fff9a0aac6a3 Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 4 Jan 2018 10:19:31 +0100 Subject: [PATCH 004/462] Cleanup --- .../InitDB.sql | 35 ------------------- .../README.md | 2 +- .../HelloWorldData.cs | 14 ++++++++ .../WorkflowCore.SampleSqlServer/Program.cs | 2 +- .../Steps/GoodbyeWorld.cs | 5 ++- .../Steps/HelloWorld.cs | 6 +++- 6 files changed, 25 insertions(+), 39 deletions(-) delete mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/InitDB.sql create mode 100644 src/samples/WorkflowCore.SampleSqlServer/HelloWorldData.cs diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/InitDB.sql b/src/providers/WorkflowCore.QueueProviders.SqlServer/InitDB.sql deleted file mode 100644 index b8320d039..000000000 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/InitDB.sql +++ /dev/null @@ -1,35 +0,0 @@ - - --- Verifica attivazione SSSB --------------------------------------------------------- --- Per disattivare ALTER DATABASE MOSE SET DISABLE_BROKER; --- Per generare nuovo id alter database MOSE set NEW_BROKER - -declare @b bit - -select @b=is_broker_enabled -from sys.databases -where name='MOSE' - -if @b=1 -begin - print 'SSSB già attivo' -end -else -begin - print 'Attivazione SSSB' - ALTER DATABASE MOSE SET ENABLE_BROKER - WITH ROLLBACK IMMEDIATE; - print 'SSSB adesso è attivo' -end - -GO - - - -CREATE MESSAGE TYPE -[//workflow-core/{instance}/workflow] -VALIDATION = WELL_FORMED_XML; - -CREATE MESSAGE TYPE -[//workflow-core/{instance}/event] -VALIDATION = WELL_FORMED_XML; \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md index 08ffa95fc..5d3633cc1 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md @@ -17,7 +17,7 @@ PM> Install-Package WorkflowCore.QueueProviders.SqlServer -Pre Use the .UseSqlServerQueue extension method when building your service provider. ```C# -services.AddWorkflow(x => x.UseSqlServerQueue(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canCreateDB)); +services.AddWorkflow(x => x.UseSqlServerQueue(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canMigrateDB)); ``` diff --git a/src/samples/WorkflowCore.SampleSqlServer/HelloWorldData.cs b/src/samples/WorkflowCore.SampleSqlServer/HelloWorldData.cs new file mode 100644 index 000000000..52d577a2d --- /dev/null +++ b/src/samples/WorkflowCore.SampleSqlServer/HelloWorldData.cs @@ -0,0 +1,14 @@ +#region using + +using System; +using System.Linq; + +#endregion + +namespace WorkflowCore.SampleSqlServer +{ + public class HelloWorldData + { + public int ID { get; set; } + } +} \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/Program.cs b/src/samples/WorkflowCore.SampleSqlServer/Program.cs index eebadc51c..319ea1f79 100644 --- a/src/samples/WorkflowCore.SampleSqlServer/Program.cs +++ b/src/samples/WorkflowCore.SampleSqlServer/Program.cs @@ -30,7 +30,7 @@ public static void Main(string[] args) host.RegisterWorkflow(); host.Start(); - host.StartWorkflow("HelloWorld", 1, null); + host.StartWorkflow("HelloWorld", 1, new HelloWorldData {ID=123}); Console.ReadLine(); host.Stop(); diff --git a/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs index b7db914b0..9e5257287 100644 --- a/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs +++ b/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs @@ -23,8 +23,11 @@ public GoodbyeWorld(ILoggerFactory loggerFactory) public override ExecutionResult Run(IStepExecutionContext context) { - Console.WriteLine("Goodbye world"); + var data = (HelloWorldData)context.Workflow.Data; + + Console.WriteLine("Goodbye world " + data.ID); _logger.LogInformation("Hi there!"); + return ExecutionResult.Next(); } } diff --git a/src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs b/src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs index 5e50d2d59..b57fdef0f 100644 --- a/src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs +++ b/src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs @@ -14,7 +14,11 @@ public class HelloWorld : StepBody { public override ExecutionResult Run(IStepExecutionContext context) { - Console.WriteLine("Hello world"); + var data = (HelloWorldData)context.Workflow.Data; + + Console.WriteLine("Hello world " + data.ID); + + data.ID++; return ExecutionResult.Next(); } From 78dabf3ef85b748d42b77ce8a350c4a87ad869dd Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Wed, 17 Jan 2018 14:02:46 +0100 Subject: [PATCH 005/462] Cleanup --- WorkflowCore.sln | 7 +++++ .../README.md | 4 +-- .../ServiceCollectionExtensions.cs | 25 +++++++++++---- .../HelloWorldData.cs | 14 --------- .../HelloWorldWorkflow.cs | 16 +++++++--- .../WorkflowCore.SampleSqlServer/Program.cs | 31 ++++++++++++++++--- .../WorkflowCore.SampleSqlServer/README.md | 6 ++-- .../Steps/GoodbyeWorld.cs | 2 +- 8 files changed, 71 insertions(+), 34 deletions(-) delete mode 100644 src/samples/WorkflowCore.SampleSqlServer/HelloWorldData.cs diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 56a7a175e..34caccf7c 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -105,6 +105,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample15", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample16", "src\samples\WorkflowCore.Sample16\WorkflowCore.Sample16.csproj", "{0C9617A9-C8B7-45F6-A54A-261A23AC881B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchPad\ScratchPad.csproj", "{6396453F-4D0E-4CD4-BC89-87E8970F2A80}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample17", "src\samples\WorkflowCore.Sample17\WorkflowCore.Sample17.csproj", "{42F475BC-95F4-42E1-8CCD-7B9C27487E33}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.QueueProviders.SqlServer", "src\providers\WorkflowCore.QueueProviders.SqlServer\WorkflowCore.QueueProviders.SqlServer.csproj", "{7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}" @@ -269,6 +271,10 @@ Global {0C9617A9-C8B7-45F6-A54A-261A23AC881B}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C9617A9-C8B7-45F6-A54A-261A23AC881B}.Release|Any CPU.ActiveCfg = Release|Any CPU {0C9617A9-C8B7-45F6-A54A-261A23AC881B}.Release|Any CPU.Build.0 = Release|Any CPU + {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Release|Any CPU.Build.0 = Release|Any CPU {42F475BC-95F4-42E1-8CCD-7B9C27487E33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {42F475BC-95F4-42E1-8CCD-7B9C27487E33}.Debug|Any CPU.Build.0 = Debug|Any CPU {42F475BC-95F4-42E1-8CCD-7B9C27487E33}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -327,6 +333,7 @@ Global {EC497168-5347-4E70-9D9E-9C2F826C1CDF} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {9B7811AC-68D6-4D19-B1E9-65423393ED83} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {0C9617A9-C8B7-45F6-A54A-261A23AC881B} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} + {6396453F-4D0E-4CD4-BC89-87E8970F2A80} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {42F475BC-95F4-42E1-8CCD-7B9C27487E33} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {4DA898F5-0299-48DA-B073-F0A4BB253B9C} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md index 5d3633cc1..86ab6cf13 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md @@ -23,13 +23,13 @@ services.AddWorkflow(x => x.UseSqlServerQueue(sp => new SqlServerQueueProvider(c ## Running test -It require a SQL Server 2016 database available with this connection string: +It require a SQL Server database (tested with 2008R2 and 2016) available with this connection string: "Server=(local);Database=wfc;User Id=wfc;Password=wfc;" and SQL Server Service Broker enabled (this command must be executed in single user mode). ```sql - ALTER DATABASE wcf SET ENABLE_BROKER + ALTER DATABASE wfc SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE; ``` diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index 7c0924c22..481bfc9da 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -12,18 +12,31 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { + /// + /// Use SQL Server as a queue provider + /// + /// + /// + /// + /// Autogenerate required service broker objects + /// public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, string workflowHostName, - bool canCreateDb = false) + bool canMigrateDb = false) { - options.UseQueueProvider(sp => - new SqlServerQueueProvider(connectionString, workflowHostName, canCreateDb /*, sp.GetService()*/)); + options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canMigrateDb)); return options; } - public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, bool canCreateDb = false) + /// + /// Use SQL Server as a queue provider (use 'default' as workflowHostName) + /// + /// + /// + /// Autogenerate required service broker objects + /// + public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, bool canMigrateDb = false) { - options.UseQueueProvider(sp => - new SqlServerQueueProvider(connectionString, "default", canCreateDb /*, sp.GetService()*/)); + options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, "default", canMigrateDb)); return options; } } diff --git a/src/samples/WorkflowCore.SampleSqlServer/HelloWorldData.cs b/src/samples/WorkflowCore.SampleSqlServer/HelloWorldData.cs deleted file mode 100644 index 52d577a2d..000000000 --- a/src/samples/WorkflowCore.SampleSqlServer/HelloWorldData.cs +++ /dev/null @@ -1,14 +0,0 @@ -#region using - -using System; -using System.Linq; - -#endregion - -namespace WorkflowCore.SampleSqlServer -{ - public class HelloWorldData - { - public int ID { get; set; } - } -} \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs b/src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs index 0296b3265..a52cc2832 100644 --- a/src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs +++ b/src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs @@ -10,14 +10,22 @@ namespace WorkflowCore.SampleSqlServer { - public class HelloWorldWorkflow : IWorkflow + public class HelloWorldData { - public void Build(IWorkflowBuilder builder) + public int ID { get; set; } + public long EventId { get; set; } + } + + public class HelloWorldWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder builder) { var stepBuilder = builder .StartWith() - .Delay(o => TimeSpan.FromSeconds(2)) - .Then(); + .WaitFor("Go", data => "0") + .Output(data => data.EventId, step => step.EventData) + .Then() + .EndWorkflow(); } public string Id => "HelloWorld"; diff --git a/src/samples/WorkflowCore.SampleSqlServer/Program.cs b/src/samples/WorkflowCore.SampleSqlServer/Program.cs index 319ea1f79..2a5e99596 100644 --- a/src/samples/WorkflowCore.SampleSqlServer/Program.cs +++ b/src/samples/WorkflowCore.SampleSqlServer/Program.cs @@ -27,13 +27,34 @@ public static void Main(string[] args) //start the workflow host var host = serviceProvider.GetService(); - host.RegisterWorkflow(); + host.RegisterWorkflow(); host.Start(); - host.StartWorkflow("HelloWorld", 1, new HelloWorldData {ID=123}); - Console.ReadLine(); - host.Stop(); + while (true) + { + Console.WriteLine("\nS:start E:event: Q:quit"); + var key = Console.ReadKey(true); + switch (key.KeyChar) + { + case 'S': + var id = host.StartWorkflow("HelloWorld", 1, new HelloWorldData {ID=123}); + Console.WriteLine(id.Result); + break; + + case 'E': + Console.Write("EventID:"); + var idIn = Console.ReadLine(); + host.PublishEvent("Go", "0", int.Parse(idIn)); + break; + + case 'Q': + host.Stop(); + Environment.Exit(0); + break; + + } + } } private static IServiceProvider ConfigureServices() @@ -41,6 +62,8 @@ private static IServiceProvider ConfigureServices() IServiceCollection services = new ServiceCollection(); services.AddLogging(); + //services.AddWorkflow(); + services.AddWorkflow(x => { x.UseSqlServer(_connectionString, false, true); diff --git a/src/samples/WorkflowCore.SampleSqlServer/README.md b/src/samples/WorkflowCore.SampleSqlServer/README.md index 5d0cee5e6..a97eb8cdf 100644 --- a/src/samples/WorkflowCore.SampleSqlServer/README.md +++ b/src/samples/WorkflowCore.SampleSqlServer/README.md @@ -1,6 +1,6 @@ # SQL Server sample -A sample to test SQL Server for persistenze, locking and queueing. +A sample to test SQL Server for persistence, locking and queueing. ```c# @@ -13,13 +13,13 @@ services.AddWorkflow(x => ); ``` -It require a SQL Server database available with this connection string: +It require a SQL Server database (tested with 2008R2 and 2016) available with this connection string: "Server=(local);Database=wfc;User Id=wfc;Password=wfc;" and SQL Server Service Broker enabled (this command must be executed in single user mode). ```sql - ALTER DATABASE wcf SET ENABLE_BROKER + ALTER DATABASE wfc SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE; ``` diff --git a/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs index 9e5257287..8ef062467 100644 --- a/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs +++ b/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs @@ -25,7 +25,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { var data = (HelloWorldData)context.Workflow.Data; - Console.WriteLine("Goodbye world " + data.ID); + Console.WriteLine($"Goodbye world {data.ID} {data.EventId}"); _logger.LogInformation("Hi there!"); return ExecutionResult.Next(); From f5d1d61631d909c29feb866c8189a5badf9c85b2 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 20 Jan 2018 11:08:54 -0800 Subject: [PATCH 006/462] Update README.md --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 9ad87fb24..4c7e9dd2f 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,28 @@ public class MyWorkflow : IWorkflow } ``` +## JSON Workflow Definitions + +Define your workflows in JSON + +```json +{ + "Id": "HelloWorld", + "Version": 1, + "Steps": [ + { + "Id": "Hello", + "StepType": "MyApp.HelloWorld, MyApp", + "NextStepId": "Bye" + }, + { + "Id": "Bye", + "StepType": "MyApp.GoodbyeWorld, MyApp" + } + ] +} +``` + ### Sample use cases * New user workflow From 24909add0ef4eb50fc2249f1a700fc5a2c3b5e32 Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 1 Feb 2018 09:52:33 +0100 Subject: [PATCH 007/462] Fix Codacy/PR Quality Review --- .../Services/SqlConnectionHelper.cs | 2 +- .../Services/SqlServerQueueProvider.cs | 6 ++---- .../SqlServerQueueProviderFixture.cs | 10 ++++++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs index f71b28a10..554ea0414 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { - public class SqlConnectionHelper + public static class SqlConnectionHelper { internal static SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name = null) { diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index fa555eb3f..c3fed9037 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -20,12 +20,9 @@ public class SqlServerQueueProvider : IQueueProvider readonly bool _canMigrateDb; readonly SqlServerNames _names; - //private ILogger _lg; - public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb /*, ILoggerFactory logFactory*/) + public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb) { - //_lg = logFactory.CreateLogger(); - _connectionString = connectionString; _workflowHostName = workflowHostName; _canMigrateDb = canMigrateDb; @@ -47,6 +44,7 @@ public async Task Start() public async Task Stop() { + // Do nothing } public void Dispose() diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs index af138446c..aa7a266c1 100644 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs @@ -36,9 +36,15 @@ public SqlServerQueueProviderFixture(ITestOutputHelper output) _qb = new SqlServerQueueProvider(connectionString, "UnitTest", true); _qb.Start().Wait(); - while (_qb.DequeueWork(QueueType.Event, CancellationToken.None).Result != null) { } + while (_qb.DequeueWork(QueueType.Event, CancellationToken.None).Result != null) + { + // Empty queue before test + } - while (_qb.DequeueWork(QueueType.Workflow, CancellationToken.None).Result != null) { } + while (_qb.DequeueWork(QueueType.Workflow, CancellationToken.None).Result != null) + { + // Empty queue before test + } } public void Dispose() From 4494595c55da5029077cb6b084f8809d1250fa57 Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 1 Feb 2018 09:55:44 +0100 Subject: [PATCH 008/462] use of the docker image instead of requiring a local instance --- .../README.md | 12 --- .../ServiceCollectionExtensions.cs | 8 +- .../Services/SqlConnectionHelper.cs | 8 ++ .../Services/SqlServerQueueProvider.cs | 13 +-- .../SqlServerQueueProviderMigrator.cs | 91 +++++++++++++++++-- .../SqlServerQueueProviderFixture.cs | 10 +- 6 files changed, 108 insertions(+), 34 deletions(-) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md index 86ab6cf13..3277962aa 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md @@ -21,15 +21,3 @@ services.AddWorkflow(x => x.UseSqlServerQueue(sp => new SqlServerQueueProvider(c ``` -## Running test - -It require a SQL Server database (tested with 2008R2 and 2016) available with this connection string: - - "Server=(local);Database=wfc;User Id=wfc;Password=wfc;" - -and SQL Server Service Broker enabled (this command must be executed in single user mode). - -```sql - ALTER DATABASE wfc SET ENABLE_BROKER - WITH ROLLBACK IMMEDIATE; -``` diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index 481bfc9da..8a9697899 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -21,9 +21,9 @@ public static class ServiceCollectionExtensions /// Autogenerate required service broker objects /// public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, string workflowHostName, - bool canMigrateDb = false) + bool canMigrateDb = false, bool canCreateDb = false) { - options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canMigrateDb)); + options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canMigrateDb, canCreateDb)); return options; } @@ -34,9 +34,9 @@ public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, st /// /// Autogenerate required service broker objects /// - public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, bool canMigrateDb = false) + public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, bool canMigrateDb = false, bool canCreateDb = false) { - options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, "default", canMigrateDb)); + options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, "default", canMigrateDb, canCreateDb)); return options; } } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs index 554ea0414..c471b9639 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs @@ -10,6 +10,14 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { public static class SqlConnectionHelper { + /// + /// + /// + /// + /// + /// + /// Add this value to parameter @name + /// internal static SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name = null) { var cmd = cn.CreateCommand(); diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index c3fed9037..df9b74089 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -18,14 +18,16 @@ public class SqlServerQueueProvider : IQueueProvider readonly string _workflowHostName; readonly bool _canMigrateDb; + readonly bool _canCreateDb; readonly SqlServerNames _names; - public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb) + public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb, bool canCreateDb) { _connectionString = connectionString; _workflowHostName = workflowHostName; _canMigrateDb = canMigrateDb; + _canCreateDb = canCreateDb; _names = new SqlServerNames(workflowHostName); IsDequeueBlocking = true; @@ -35,11 +37,10 @@ public SqlServerQueueProvider(string connectionString, string workflowHostName, public async Task Start() { - if (_canMigrateDb) - { - var mig = new SqlServerQueueProviderMigrator(_connectionString, _workflowHostName); - mig.MigrateDb(); - } + var mig = new SqlServerQueueProviderMigrator(_connectionString, _workflowHostName); + + if (_canCreateDb) mig.CreateDb(); + if (_canMigrateDb) mig.MigrateDb(); } public async Task Stop() diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index c5ddda6f0..772f87acc 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -3,6 +3,7 @@ using System; using System.Data.SqlClient; using System.Linq; +using System.Text.RegularExpressions; #endregion @@ -22,6 +23,8 @@ public SqlServerQueueProviderMigrator(string connectionString, string workflowHo } + #region Migrate + internal void MigrateDb() { var cn = new SqlConnection(_connectionString); @@ -30,7 +33,6 @@ internal void MigrateDb() cn.Open(); var tx = cn.BeginTransaction(); - EnableBroker(cn, tx); CreateMessageType(cn, tx, _names.WorkflowMessageType); CreateMessageType(cn, tx, _names.EventMessageType); @@ -121,18 +123,93 @@ private static void CreateMessageType(SqlConnection cn, SqlTransaction tx, strin } } - private static void EnableBroker(SqlConnection cn, SqlTransaction tx) + #endregion + + public void CreateDb() { - var cmdtext = @"select is_broker_enabled from sys.databases where name = @name"; - using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, cn.Database)) + var pattern = ";Database=(.[^;]+);"; + + var regex = new Regex(pattern); + var db = regex.Match(_connectionString).Groups[1].Value; + + var masterCn = _connectionString.Replace(db, "master"); + + var dbPresente = false; + var cn = new SqlConnection(masterCn); + try + { + cn.Open(); + + var cmd = cn.CreateCommand(); + cmd.CommandText = "select name from sys.databases where name = @dbname"; + cmd.Parameters.AddWithValue("@dbname", db); + var found=cmd.ExecuteScalar(); + dbPresente = (found != null); + } + finally + { + cn.Close(); + } + + if (!dbPresente) + { + cn = new SqlConnection(masterCn); + try + { + cn.Open(); + //var tx = cn.BeginTransaction(); + + var cmd = cn.CreateCommand(); + //cmd.Transaction = tx; + cmd.CommandText = "create database [" + db + "]"; + cmd.ExecuteNonQuery(); + } + finally + { + cn.Close(); + } + } + + EnableBroker(masterCn, db); + } + + private static void EnableBroker(string masterCn, string db) + { + var cn = new SqlConnection(masterCn); + try { + cn.Open(); + var tx = cn.BeginTransaction(); + + var cmdtext = @"select is_broker_enabled from sys.databases where name = @name"; + var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, db); + bool isBrokerEnabled = (bool)cmd.ExecuteScalar(); if (isBrokerEnabled) return; + + tx.Commit(); } + finally + { + cn.Close(); + } + + cn = new SqlConnection(masterCn); + try + { + cn.Open(); + var tx = cn.BeginTransaction(); + + var cmdtext = $"ALTER DATABASE [{db}] SET ENABLE_BROKER;"; + var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext); - var msg = - $"Service Broker not enabled on database {cn.Database}. Execute 'ALTER DATABASE {cn.Database} SET ENABLE_BROKER' in single user mode "; - throw new InvalidOperationException(msg); + cmd.ExecuteScalar(); + tx.Commit(); + } + finally + { + cn.Close(); + } } } } \ No newline at end of file diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs index aa7a266c1..1b2f2233f 100644 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs @@ -20,20 +20,20 @@ namespace WorkflowCore.Tests.SqlServer { - [Collection("SqlServerBroker collection")] + [Collection("SqlServer collection")] public class SqlServerQueueProviderFixture : IDisposable { #region Init - readonly SqlServerQueueProvider _qb; + private readonly SqlServerQueueProvider _qb; private readonly ITestOutputHelper _console; - public SqlServerQueueProviderFixture(ITestOutputHelper output) + public SqlServerQueueProviderFixture(ITestOutputHelper output, SqlDockerSetup setup) { _console = output; - var connectionString = "Server=(local);Database=wfc;User Id=wfc;Password=wfc;"; + var connectionString = SqlDockerSetup.ConnectionString; - _qb = new SqlServerQueueProvider(connectionString, "UnitTest", true); + _qb = new SqlServerQueueProvider(connectionString, "UnitTest", true, true); _qb.Start().Wait(); while (_qb.DequeueWork(QueueType.Event, CancellationToken.None).Result != null) From c31ed27e54372e77ab4a02e9b118008ee5acdcfe Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 1 Feb 2018 10:01:10 +0100 Subject: [PATCH 009/462] Change method name to UseSqlServerBroker --- .../ServiceCollectionExtensions.cs | 4 ++-- src/samples/WorkflowCore.SampleSqlServer/Program.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index 8a9697899..7a4798c3e 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -20,7 +20,7 @@ public static class ServiceCollectionExtensions /// /// Autogenerate required service broker objects /// - public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, string workflowHostName, + public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, string connectionString, string workflowHostName, bool canMigrateDb = false, bool canCreateDb = false) { options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canMigrateDb, canCreateDb)); @@ -34,7 +34,7 @@ public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, st /// /// Autogenerate required service broker objects /// - public static WorkflowOptions UseSqlServerQueue(this WorkflowOptions options, string connectionString, bool canMigrateDb = false, bool canCreateDb = false) + public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, string connectionString, bool canMigrateDb = false, bool canCreateDb = false) { options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, "default", canMigrateDb, canCreateDb)); return options; diff --git a/src/samples/WorkflowCore.SampleSqlServer/Program.cs b/src/samples/WorkflowCore.SampleSqlServer/Program.cs index 2a5e99596..bfe1c6c4e 100644 --- a/src/samples/WorkflowCore.SampleSqlServer/Program.cs +++ b/src/samples/WorkflowCore.SampleSqlServer/Program.cs @@ -68,7 +68,7 @@ private static IServiceProvider ConfigureServices() { x.UseSqlServer(_connectionString, false, true); x.UseSqlServerLocking(_connectionString); - x.UseSqlServerQueue(_connectionString, true); + x.UseSqlServerBroker(_connectionString, true); } ); From 9c56a5f2920aadb19bd8c114e59d646d2b0df42f Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 1 Feb 2018 10:36:59 +0100 Subject: [PATCH 010/462] Change SqlServerNames to BrokerNamesProvider --- ...lServerNames.cs => BrokerNamesProvider.cs} | 20 +++++++++++++++++-- .../Services/SqlServerQueueProvider.cs | 4 ++-- .../SqlServerQueueProviderMigrator.cs | 10 ++++------ 3 files changed, 24 insertions(+), 10 deletions(-) rename src/providers/WorkflowCore.QueueProviders.SqlServer/Services/{SqlServerNames.cs => BrokerNamesProvider.cs} (71%) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs similarity index 71% rename from src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs rename to src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs index 2b47b9d0a..e92072428 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerNames.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs @@ -7,19 +7,35 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { + /// + /// Base interface for + /// + public interface IBrokerNamesProvider { + string WorkflowContractName { get; } + string TargetEventServiceName { get; } + string InitiatorEventServiceName { get; } + string WorkflowQueueName { get; } + string EventQueueName { get; } + string TargetWorkflowServiceName { get; } + string InitiatorWorkflowServiceName { get; } + string EventContractName { get; } + string EventMessageType { get; } + string WorkflowMessageType { get; } + } + /// /// Build names for SSSB objects /// /// /// Message type and contract are global, service name and queue different for every workflow host /// - public class SqlServerNames + public class BrokerNamesProvider : IBrokerNamesProvider { /// /// ctor /// /// - public SqlServerNames(string workflowHostName) + public BrokerNamesProvider(string workflowHostName) { WorkflowMessageType = "//workflow-core/workflow"; EventMessageType = "//workflow-core/event"; diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index df9b74089..06a8bda1b 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -20,7 +20,7 @@ public class SqlServerQueueProvider : IQueueProvider readonly bool _canMigrateDb; readonly bool _canCreateDb; - readonly SqlServerNames _names; + readonly IBrokerNamesProvider _names; public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb, bool canCreateDb) { @@ -28,7 +28,7 @@ public SqlServerQueueProvider(string connectionString, string workflowHostName, _workflowHostName = workflowHostName; _canMigrateDb = canMigrateDb; _canCreateDb = canCreateDb; - _names = new SqlServerNames(workflowHostName); + _names = new BrokerNamesProvider(workflowHostName); IsDequeueBlocking = true; } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index 772f87acc..892d49a85 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -13,13 +13,13 @@ public class SqlServerQueueProviderMigrator { readonly string _connectionString; - readonly SqlServerNames _names; + readonly IBrokerNamesProvider _names; public SqlServerQueueProviderMigrator(string connectionString, string workflowHostName) { _connectionString = connectionString; - _names = new SqlServerNames(workflowHostName); + _names = new BrokerNamesProvider(workflowHostName); } @@ -134,7 +134,7 @@ public void CreateDb() var masterCn = _connectionString.Replace(db, "master"); - var dbPresente = false; + bool dbPresente; var cn = new SqlConnection(masterCn); try { @@ -157,10 +157,8 @@ public void CreateDb() try { cn.Open(); - //var tx = cn.BeginTransaction(); - + var cmd = cn.CreateCommand(); - //cmd.Transaction = tx; cmd.CommandText = "create database [" + db + "]"; cmd.ExecuteNonQuery(); } From b36e2462d7fa2b33373d786a03ee0fe238e27912 Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 1 Feb 2018 10:52:43 +0100 Subject: [PATCH 011/462] Removed SampleSqlServer --- .../HelloWorldWorkflow.cs | 35 -------- .../WorkflowCore.SampleSqlServer/Program.cs | 86 ------------------- .../WorkflowCore.SampleSqlServer/README.md | 25 ------ .../Steps/GoodbyeWorld.cs | 34 -------- .../Steps/HelloWorld.cs | 26 ------ .../WorkflowCore.SampleSqlServer.csproj | 32 ------- 6 files changed, 238 deletions(-) delete mode 100644 src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs delete mode 100644 src/samples/WorkflowCore.SampleSqlServer/Program.cs delete mode 100644 src/samples/WorkflowCore.SampleSqlServer/README.md delete mode 100644 src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs delete mode 100644 src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs delete mode 100644 src/samples/WorkflowCore.SampleSqlServer/WorkflowCore.SampleSqlServer.csproj diff --git a/src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs b/src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs deleted file mode 100644 index a52cc2832..000000000 --- a/src/samples/WorkflowCore.SampleSqlServer/HelloWorldWorkflow.cs +++ /dev/null @@ -1,35 +0,0 @@ -#region using - -using System; -using System.Linq; - -using WorkflowCore.Interface; -using WorkflowCore.SampleSqlServer.Steps; - -#endregion - -namespace WorkflowCore.SampleSqlServer -{ - public class HelloWorldData - { - public int ID { get; set; } - public long EventId { get; set; } - } - - public class HelloWorldWorkflow : IWorkflow - { - public void Build(IWorkflowBuilder builder) - { - var stepBuilder = builder - .StartWith() - .WaitFor("Go", data => "0") - .Output(data => data.EventId, step => step.EventData) - .Then() - .EndWorkflow(); - } - - public string Id => "HelloWorld"; - - public int Version => 1; - } -} \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/Program.cs b/src/samples/WorkflowCore.SampleSqlServer/Program.cs deleted file mode 100644 index bfe1c6c4e..000000000 --- a/src/samples/WorkflowCore.SampleSqlServer/Program.cs +++ /dev/null @@ -1,86 +0,0 @@ -#region using - -using System; -using System.Linq; - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -using WorkflowCore.Interface; -using WorkflowCore.SampleSqlServer.Steps; - -#endregion - -namespace WorkflowCore.SampleSqlServer -{ - class Program - { - private static readonly string _connectionString = "Server=(local);Database=wfc;User Id=wfc;Password=wfc;"; - private static ILogger _lg; - - public static void Main(string[] args) - { - IServiceProvider serviceProvider = ConfigureServices(); - - _lg = serviceProvider.GetService().CreateLogger(); - _lg.LogDebug("->"); - - //start the workflow host - var host = serviceProvider.GetService(); - host.RegisterWorkflow(); - host.Start(); - - - while (true) - { - Console.WriteLine("\nS:start E:event: Q:quit"); - var key = Console.ReadKey(true); - switch (key.KeyChar) - { - case 'S': - var id = host.StartWorkflow("HelloWorld", 1, new HelloWorldData {ID=123}); - Console.WriteLine(id.Result); - break; - - case 'E': - Console.Write("EventID:"); - var idIn = Console.ReadLine(); - host.PublishEvent("Go", "0", int.Parse(idIn)); - break; - - case 'Q': - host.Stop(); - Environment.Exit(0); - break; - - } - } - } - - private static IServiceProvider ConfigureServices() - { - IServiceCollection services = new ServiceCollection(); - services.AddLogging(); - - //services.AddWorkflow(); - - services.AddWorkflow(x => - { - x.UseSqlServer(_connectionString, false, true); - x.UseSqlServerLocking(_connectionString); - x.UseSqlServerBroker(_connectionString, true); - } - ); - - services.AddTransient(); - - var serviceProvider = services.BuildServiceProvider(); - - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(LogLevel.Debug); - - return serviceProvider; - } - } -} \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/README.md b/src/samples/WorkflowCore.SampleSqlServer/README.md deleted file mode 100644 index a97eb8cdf..000000000 --- a/src/samples/WorkflowCore.SampleSqlServer/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# SQL Server sample - -A sample to test SQL Server for persistence, locking and queueing. - - -```c# -services.AddWorkflow(x => - { - x.UseSqlServerQueue(_connectionString, "SampleSqlServer", true); - x.UseSqlServer(_connectionString, false, true); - x.UseSqlServerLocking(_connectionString); - } - ); -``` - -It require a SQL Server database (tested with 2008R2 and 2016) available with this connection string: - - "Server=(local);Database=wfc;User Id=wfc;Password=wfc;" - -and SQL Server Service Broker enabled (this command must be executed in single user mode). - -```sql - ALTER DATABASE wfc SET ENABLE_BROKER - WITH ROLLBACK IMMEDIATE; -``` diff --git a/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs deleted file mode 100644 index 8ef062467..000000000 --- a/src/samples/WorkflowCore.SampleSqlServer/Steps/GoodbyeWorld.cs +++ /dev/null @@ -1,34 +0,0 @@ -#region using - -using System; -using System.Linq; - -using Microsoft.Extensions.Logging; - -using WorkflowCore.Interface; -using WorkflowCore.Models; - -#endregion - -namespace WorkflowCore.SampleSqlServer.Steps -{ - public class GoodbyeWorld : StepBody - { - private readonly ILogger _logger; - - public GoodbyeWorld(ILoggerFactory loggerFactory) - { - _logger = loggerFactory.CreateLogger(); - } - - public override ExecutionResult Run(IStepExecutionContext context) - { - var data = (HelloWorldData)context.Workflow.Data; - - Console.WriteLine($"Goodbye world {data.ID} {data.EventId}"); - _logger.LogInformation("Hi there!"); - - return ExecutionResult.Next(); - } - } -} \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs b/src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs deleted file mode 100644 index b57fdef0f..000000000 --- a/src/samples/WorkflowCore.SampleSqlServer/Steps/HelloWorld.cs +++ /dev/null @@ -1,26 +0,0 @@ -#region using - -using System; -using System.Linq; - -using WorkflowCore.Interface; -using WorkflowCore.Models; - -#endregion - -namespace WorkflowCore.SampleSqlServer.Steps -{ - public class HelloWorld : StepBody - { - public override ExecutionResult Run(IStepExecutionContext context) - { - var data = (HelloWorldData)context.Workflow.Data; - - Console.WriteLine("Hello world " + data.ID); - - data.ID++; - - return ExecutionResult.Next(); - } - } -} \ No newline at end of file diff --git a/src/samples/WorkflowCore.SampleSqlServer/WorkflowCore.SampleSqlServer.csproj b/src/samples/WorkflowCore.SampleSqlServer/WorkflowCore.SampleSqlServer.csproj deleted file mode 100644 index 3b1b6d98c..000000000 --- a/src/samples/WorkflowCore.SampleSqlServer/WorkflowCore.SampleSqlServer.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - Exe - netcoreapp2.0 - - - - - - - - - - - - - - - - - ..\..\..\..\..\Users\r.paterlini\.nuget\packages\microsoft.extensions.dependencyinjection\1.1.1\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll - - - ..\..\..\..\..\Users\r.paterlini\.nuget\packages\microsoft.extensions.dependencyinjection.abstractions\1.1.1\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - - ..\..\..\..\..\Users\r.paterlini\.nuget\packages\microsoft.extensions.logging.abstractions\1.1.2\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll - - - - From f4536454674eaea2baa5d892ca049f7b5277a80d Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 1 Feb 2018 12:20:44 +0100 Subject: [PATCH 012/462] Moved sql text to resource file --- WorkflowCore.sln | 9 +-- .../Services/DequeueWork.sql | 16 +++++ .../Services/QueueWork.sql | 17 ++++++ .../Services/SqlServerQueueProvider.cs | 61 ++++++++----------- ...rkflowCore.QueueProviders.SqlServer.csproj | 10 +++ 5 files changed, 68 insertions(+), 45 deletions(-) create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/DequeueWork.sql create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueWork.sql diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 34caccf7c..9a7d80b66 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 +VisualStudioVersion = 15.0.27130.2026 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF47161E-E399-451C-BDE8-E92AAD3BD761}" EndProject @@ -111,8 +111,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample17", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.QueueProviders.SqlServer", "src\providers\WorkflowCore.QueueProviders.SqlServer\WorkflowCore.QueueProviders.SqlServer.csproj", "{7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.SampleSqlServer", "src\samples\WorkflowCore.SampleSqlServer\WorkflowCore.SampleSqlServer.csproj", "{4DA898F5-0299-48DA-B073-F0A4BB253B9C}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -283,10 +281,6 @@ Global {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Debug|Any CPU.Build.0 = Debug|Any CPU {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Release|Any CPU.ActiveCfg = Release|Any CPU {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Release|Any CPU.Build.0 = Release|Any CPU - {4DA898F5-0299-48DA-B073-F0A4BB253B9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4DA898F5-0299-48DA-B073-F0A4BB253B9C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4DA898F5-0299-48DA-B073-F0A4BB253B9C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4DA898F5-0299-48DA-B073-F0A4BB253B9C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -336,7 +330,6 @@ Global {6396453F-4D0E-4CD4-BC89-87E8970F2A80} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {42F475BC-95F4-42E1-8CCD-7B9C27487E33} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} - {4DA898F5-0299-48DA-B073-F0A4BB253B9C} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/DequeueWork.sql b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/DequeueWork.sql new file mode 100644 index 000000000..04d061d86 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/DequeueWork.sql @@ -0,0 +1,16 @@ +DECLARE @TargetDlgHandle UNIQUEIDENTIFIER +DECLARE @Message varbinary(max) +DECLARE @MessageName Sysname + +BEGIN TRAN; + +WAITFOR ( + RECEIVE TOP(1) + @TargetDlgHandle=Conversation_Handle + ,@Message=Message_Body + ,@MessageName=Message_Type_Name + FROM [{queueName}]), +TIMEOUT 1000; + +SELECT cast(@Message as nvarchar(max)) +COMMIT TRAN \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueWork.sql b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueWork.sql new file mode 100644 index 000000000..d662fd3e4 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueWork.sql @@ -0,0 +1,17 @@ +DECLARE @InitDlgHandle UNIQUEIDENTIFIER +BEGIN TRAN + +BEGIN DIALOG @InitDlgHandle +FROM SERVICE +[{initiatorService}] +TO SERVICE +'{targetService}' +ON CONTRACT +[{contractName}] +WITH ENCRYPTION=OFF; + +SEND ON CONVERSATION @InitDlgHandle +MESSAGE TYPE [{msgType}] +(@RequestMessage); + +COMMIT TRAN \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index 06a8bda1b..85c6bc91d 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -2,7 +2,9 @@ using System; using System.Data.SqlClient; +using System.IO; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -22,6 +24,9 @@ public class SqlServerQueueProvider : IQueueProvider readonly IBrokerNamesProvider _names; + private readonly string _queueWork; + private readonly string _dequeueWork; + public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb, bool canCreateDb) { _connectionString = connectionString; @@ -31,8 +36,22 @@ public SqlServerQueueProvider(string connectionString, string workflowHostName, _names = new BrokerNamesProvider(workflowHostName); IsDequeueBlocking = true; + + _queueWork = GetFromResource("QueueWork"); + _dequeueWork = GetFromResource("DequeueWork"); + } + + private static string GetFromResource(string file) + { + var resName = $"WorkflowCore.QueueProviders.SqlServer.Services.{file}.sql"; + + using (var reader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(resName))) + { + return reader.ReadToEnd(); + } } + public bool IsDequeueBlocking { get; } public async Task Start() @@ -82,25 +101,10 @@ public async Task QueueWork(string id, QueueType queue) contractName = _names.EventContractName; } - var sql = $@" -DECLARE @InitDlgHandle UNIQUEIDENTIFIER -BEGIN TRAN - -BEGIN DIALOG @InitDlgHandle -FROM SERVICE -[{initiatorService}] -TO SERVICE -'{targetService}' -ON CONTRACT -[{contractName}] -WITH ENCRYPTION=OFF; - -SEND ON CONVERSATION @InitDlgHandle -MESSAGE TYPE [{msgType}] -(@RequestMessage); - -COMMIT TRAN -"; + var sql = _queueWork.Replace("{initiatorService}",initiatorService) + .Replace("{targetService}",targetService) + .Replace("{contractName}", contractName) + .Replace("{msgType}", msgType); cn = new SqlConnection(_connectionString); cn.Open(); @@ -129,24 +133,7 @@ public async Task DequeueWork(QueueType queue, CancellationToken cancell { var queueName = queue == QueueType.Workflow ? _names.WorkflowQueueName : _names.EventQueueName; - var sql = $@" -DECLARE @TargetDlgHandle UNIQUEIDENTIFIER -DECLARE @Message varbinary(max) -DECLARE @MessageName Sysname - -BEGIN TRAN; - -WAITFOR ( - RECEIVE TOP(1) - @TargetDlgHandle=Conversation_Handle - ,@Message=Message_Body - ,@MessageName=Message_Type_Name - FROM [{queueName}]), -TIMEOUT 1000; - -SELECT cast(@Message as nvarchar(max)) -COMMIT TRAN -"; + var sql = _dequeueWork.Replace("{queueName}", queueName); cn = new SqlConnection(_connectionString); cn.Open(); diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index a37ffaa9f..9502c7200 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -4,6 +4,16 @@ netstandard2.0 + + + + + + + + + + From 23db69ab8c09297ea0ca8f7c78b317a355bcc91d Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 1 Feb 2018 12:30:30 +0100 Subject: [PATCH 013/462] Changed test names --- .../SqlServerQueueProviderFixture.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs index 1b2f2233f..af82ea3e8 100644 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs @@ -57,7 +57,7 @@ public void Dispose() #region QueueDeque [Fact] - public void QueueDequeTest() + public void ShouldEnqueueAndDequeueAMessage() { var id = Guid.NewGuid().ToString(); @@ -78,7 +78,7 @@ void DoTest(string id, QueueType queueType) #endregion [Fact] - public void MultiTest() + public void ShouldEnqueueAndDequeueManyMessageOnManyThread() { const int countEvent = 250; const int countThread = 10; From f0ceadc0cc0fd748bd0dcdeed0cf9bde3452b26a Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Fri, 2 Feb 2018 14:38:27 +0100 Subject: [PATCH 014/462] Create object using IoC --- .../ServiceCollectionExtensions.cs | 11 +++- ...nectionHelper.cs => SqlCommandExecutor.cs} | 16 ++++- .../Services/SqlServerQueueProvider.cs | 38 +++++++----- .../SqlServerQueueProviderMigrator.cs | 62 ++++++++++--------- .../Services/SqlServerQueueProviderOption.cs | 25 ++++++++ ...rkflowCore.QueueProviders.SqlServer.csproj | 1 + .../SqlServerQueueProviderFixture.cs | 16 ++++- 7 files changed, 118 insertions(+), 51 deletions(-) rename src/providers/WorkflowCore.QueueProviders.SqlServer/Services/{SqlConnectionHelper.cs => SqlCommandExecutor.cs} (54%) create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderOption.cs diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index 7a4798c3e..f2c1c1deb 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ using System.Linq; using WorkflowCore.Models; +using WorkflowCore.QueueProviders.SqlServer; using WorkflowCore.QueueProviders.SqlServer.Services; #endregion @@ -19,11 +20,16 @@ public static class ServiceCollectionExtensions /// /// /// Autogenerate required service broker objects + /// /// public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, string connectionString, string workflowHostName, bool canMigrateDb = false, bool canCreateDb = false) { - options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canMigrateDb, canCreateDb)); + options.UseQueueProvider(sp => + { + var opt = new SqlServerQueueProviderOption(connectionString, workflowHostName, canMigrateDb, canCreateDb); + return new SqlServerQueueProvider(sp,opt); + }); return options; } @@ -33,10 +39,11 @@ public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, s /// /// /// Autogenerate required service broker objects + /// /// public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, string connectionString, bool canMigrateDb = false, bool canCreateDb = false) { - options.UseQueueProvider(sp => new SqlServerQueueProvider(connectionString, "default", canMigrateDb, canCreateDb)); + UseSqlServerBroker(options, connectionString, "default", canMigrateDb, canCreateDb); return options; } } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs similarity index 54% rename from src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs rename to src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs index c471b9639..e05510cfa 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlConnectionHelper.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs @@ -8,7 +8,19 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { - public static class SqlConnectionHelper + public interface ISqlCommandExecutor { + /// + /// + /// + /// + /// + /// + /// Add this value to parameter @name + /// + SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name = null); + } + + public class SqlCommandExecutor : ISqlCommandExecutor { /// /// @@ -18,7 +30,7 @@ public static class SqlConnectionHelper /// /// Add this value to parameter @name /// - internal static SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name = null) + public SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name = null) { var cmd = cn.CreateCommand(); cmd.Transaction = tx; diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index 85c6bc91d..df563e249 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -8,6 +8,8 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; + using WorkflowCore.Interface; #endregion @@ -17,23 +19,29 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services public class SqlServerQueueProvider : IQueueProvider { readonly string _connectionString; - readonly string _workflowHostName; readonly bool _canMigrateDb; readonly bool _canCreateDb; - readonly IBrokerNamesProvider _names; + private readonly IBrokerNamesProvider _names; + private readonly ISqlServerQueueProviderMigrator _migrator; + private readonly ISqlCommandExecutor _sqlCommandExecutor; private readonly string _queueWork; private readonly string _dequeueWork; - public SqlServerQueueProvider(string connectionString, string workflowHostName, bool canMigrateDb, bool canCreateDb) + public SqlServerQueueProvider(IServiceProvider serviceProvider, SqlServerQueueProviderOption opt) { - _connectionString = connectionString; - _workflowHostName = workflowHostName; - _canMigrateDb = canMigrateDb; - _canCreateDb = canCreateDb; - _names = new BrokerNamesProvider(workflowHostName); + _connectionString = opt.ConnectionString; + _canMigrateDb = opt.CanMigrateDb; + _canCreateDb = opt.CanCreateDb; + + _names = serviceProvider.GetService() + ?? new BrokerNamesProvider(opt.WorkflowHostName); + _sqlCommandExecutor = serviceProvider.GetService() + ?? new SqlCommandExecutor(); + _migrator = serviceProvider.GetService() + ?? new SqlServerQueueProviderMigrator(opt.ConnectionString, _names, _sqlCommandExecutor); IsDequeueBlocking = true; @@ -56,10 +64,8 @@ private static string GetFromResource(string file) public async Task Start() { - var mig = new SqlServerQueueProviderMigrator(_connectionString, _workflowHostName); - - if (_canCreateDb) mig.CreateDb(); - if (_canMigrateDb) mig.MigrateDb(); + if (_canCreateDb) _migrator.CreateDb(); + if (_canMigrateDb) _migrator.MigrateDb(); } public async Task Stop() @@ -101,14 +107,14 @@ public async Task QueueWork(string id, QueueType queue) contractName = _names.EventContractName; } - var sql = _queueWork.Replace("{initiatorService}",initiatorService) - .Replace("{targetService}",targetService) + var sql = _queueWork.Replace("{initiatorService}", initiatorService) + .Replace("{targetService}", targetService) .Replace("{contractName}", contractName) .Replace("{msgType}", msgType); cn = new SqlConnection(_connectionString); cn.Open(); - using (var cmd = SqlConnectionHelper.CreateCommand(cn, null, sql)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, null, sql)) { cmd.Parameters.AddWithValue("@RequestMessage", id); await cmd.ExecuteNonQueryAsync(); @@ -137,7 +143,7 @@ public async Task DequeueWork(QueueType queue, CancellationToken cancell cn = new SqlConnection(_connectionString); cn.Open(); - using (var cmd = SqlConnectionHelper.CreateCommand(cn, null, sql)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, null, sql)) { var msg = await cmd.ExecuteScalarAsync(cancellationToken); return msg is DBNull ? null : (string)msg; diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index 892d49a85..d4048d679 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -9,23 +9,31 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { - public class SqlServerQueueProviderMigrator + public interface ISqlServerQueueProviderMigrator + { + void MigrateDb(); + void CreateDb(); + } + + public class SqlServerQueueProviderMigrator : ISqlServerQueueProviderMigrator { readonly string _connectionString; readonly IBrokerNamesProvider _names; + private readonly ISqlCommandExecutor _sqlCommandExecutor; - public SqlServerQueueProviderMigrator(string connectionString, string workflowHostName) + public SqlServerQueueProviderMigrator(string connectionString, IBrokerNamesProvider names, ISqlCommandExecutor sqlCommandExecutor) { _connectionString = connectionString; - _names = new BrokerNamesProvider(workflowHostName); + _names = names; + _sqlCommandExecutor = sqlCommandExecutor; } #region Migrate - internal void MigrateDb() + public void MigrateDb() { var cn = new SqlConnection(_connectionString); try @@ -55,10 +63,10 @@ internal void MigrateDb() } } - private static void CreateService(SqlConnection cn, SqlTransaction tx, string name, string queueName, string contractName) + private void CreateService(SqlConnection cn, SqlTransaction tx, string name, string queueName, string contractName) { var cmdtext = @"select name from sys.services where name=@name"; - using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, name)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext, name)) { var n = (string)cmd.ExecuteScalar(); @@ -66,16 +74,16 @@ private static void CreateService(SqlConnection cn, SqlTransaction tx, string na } cmdtext = $"CREATE SERVICE [{name}] ON QUEUE [{queueName}]([{contractName}]);"; - using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) { cmd.ExecuteNonQuery(); } } - private static void CreateQueue(SqlConnection cn, SqlTransaction tx, string queueName) + private void CreateQueue(SqlConnection cn, SqlTransaction tx, string queueName) { var cmdtext = @"select name from sys.service_queues where name=@name"; - using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, queueName)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext, queueName)) { var n = (string)cmd.ExecuteScalar(); @@ -83,16 +91,16 @@ private static void CreateQueue(SqlConnection cn, SqlTransaction tx, string queu } cmdtext = $"CREATE QUEUE [{queueName}];"; - using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) { cmd.ExecuteNonQuery(); } } - private static void CreateContract(SqlConnection cn, SqlTransaction tx, string contractName, string messageName) + private void CreateContract(SqlConnection cn, SqlTransaction tx, string contractName, string messageName) { var cmdtext = @"select name from sys.service_contracts where name=@name"; - using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, contractName)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext, contractName)) { var n = (string)cmd.ExecuteScalar(); @@ -100,16 +108,16 @@ private static void CreateContract(SqlConnection cn, SqlTransaction tx, string c } cmdtext = $"CREATE CONTRACT [{contractName}] ( [{messageName}] SENT BY INITIATOR);"; - using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) { cmd.ExecuteNonQuery(); } } - private static void CreateMessageType(SqlConnection cn, SqlTransaction tx, string message) + private void CreateMessageType(SqlConnection cn, SqlTransaction tx, string message) { var cmdtext = @"select name from sys.service_message_types where name=@name"; - using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, message)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext, message)) { var n = (string)cmd.ExecuteScalar(); @@ -117,7 +125,7 @@ private static void CreateMessageType(SqlConnection cn, SqlTransaction tx, strin } cmdtext = $"CREATE MESSAGE TYPE [{message}] VALIDATION = NONE;"; - using (var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) { cmd.ExecuteNonQuery(); } @@ -143,10 +151,9 @@ public void CreateDb() var cmd = cn.CreateCommand(); cmd.CommandText = "select name from sys.databases where name = @dbname"; cmd.Parameters.AddWithValue("@dbname", db); - var found=cmd.ExecuteScalar(); + var found = cmd.ExecuteScalar(); dbPresente = (found != null); - } - finally + } finally { cn.Close(); } @@ -157,12 +164,11 @@ public void CreateDb() try { cn.Open(); - + var cmd = cn.CreateCommand(); cmd.CommandText = "create database [" + db + "]"; cmd.ExecuteNonQuery(); - } - finally + } finally { cn.Close(); } @@ -171,7 +177,7 @@ public void CreateDb() EnableBroker(masterCn, db); } - private static void EnableBroker(string masterCn, string db) + private void EnableBroker(string masterCn, string db) { var cn = new SqlConnection(masterCn); try @@ -180,14 +186,13 @@ private static void EnableBroker(string masterCn, string db) var tx = cn.BeginTransaction(); var cmdtext = @"select is_broker_enabled from sys.databases where name = @name"; - var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext, db); + var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext, db); bool isBrokerEnabled = (bool)cmd.ExecuteScalar(); if (isBrokerEnabled) return; tx.Commit(); - } - finally + } finally { cn.Close(); } @@ -199,12 +204,11 @@ private static void EnableBroker(string masterCn, string db) var tx = cn.BeginTransaction(); var cmdtext = $"ALTER DATABASE [{db}] SET ENABLE_BROKER;"; - var cmd = SqlConnectionHelper.CreateCommand(cn, tx, cmdtext); + var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext); cmd.ExecuteScalar(); tx.Commit(); - } - finally + } finally { cn.Close(); } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderOption.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderOption.cs new file mode 100644 index 000000000..a5fce583d --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderOption.cs @@ -0,0 +1,25 @@ +#region using + +using System; +using System.Linq; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer.Services +{ + public class SqlServerQueueProviderOption + { + public SqlServerQueueProviderOption(string connectionString, string workflowHostName, bool canMigrateDb, bool canCreateDb) + { + ConnectionString = connectionString; + WorkflowHostName = workflowHostName; + CanMigrateDb = canMigrateDb; + CanCreateDb = canCreateDb; + } + + public string ConnectionString { get; private set; } + public string WorkflowHostName { get; private set; } + public bool CanMigrateDb { get; private set; } + public bool CanCreateDb { get; private set; } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 9502c7200..7b0f172e5 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -7,6 +7,7 @@ + diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs index af82ea3e8..97c1db557 100644 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs @@ -10,6 +10,9 @@ using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + using WorkflowCore.Interface; using WorkflowCore.QueueProviders.SqlServer.Services; @@ -27,13 +30,16 @@ public class SqlServerQueueProviderFixture : IDisposable private readonly SqlServerQueueProvider _qb; private readonly ITestOutputHelper _console; + private static ServiceProvider _serviceProvider; public SqlServerQueueProviderFixture(ITestOutputHelper output, SqlDockerSetup setup) { _console = output; - var connectionString = SqlDockerSetup.ConnectionString; + var connectionString = SqlDockerSetup.ConnectionString; + ConfigureServices(); - _qb = new SqlServerQueueProvider(connectionString, "UnitTest", true, true); + var opt = new SqlServerQueueProviderOption(connectionString, "UnitTest", true, true); + _qb = new SqlServerQueueProvider(_serviceProvider, opt); _qb.Start().Wait(); while (_qb.DequeueWork(QueueType.Event, CancellationToken.None).Result != null) @@ -47,6 +53,12 @@ public SqlServerQueueProviderFixture(ITestOutputHelper output, SqlDockerSetup se } } + private static void ConfigureServices() + { + IServiceCollection services = new ServiceCollection(); + _serviceProvider = services.BuildServiceProvider(); + } + public void Dispose() { _qb.Dispose(); From b81bc98b9dda5ea0cd2fb51c3cc264b684b4b362 Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Mon, 5 Feb 2018 11:37:36 +0100 Subject: [PATCH 015/462] Fact moved to BaseQueueProviderFixture --- .../SqlServerQueueProviderFixture.cs | 125 +------------- .../BaseQueueProviderFixture.cs | 154 ++++++++++++++++++ 2 files changed, 161 insertions(+), 118 deletions(-) create mode 100644 test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs index 97c1db557..5483d6782 100644 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs @@ -1,20 +1,12 @@ #region using using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -using FluentAssertions; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using WorkflowCore.Interface; using WorkflowCore.QueueProviders.SqlServer.Services; +using WorkflowCore.UnitTests; using Xunit; using Xunit.Abstractions; @@ -24,33 +16,21 @@ namespace WorkflowCore.Tests.SqlServer { [Collection("SqlServer collection")] - public class SqlServerQueueProviderFixture : IDisposable + public class SqlServerQueueProviderFixture : BaseQueueProviderFixture, IDisposable { - #region Init - - private readonly SqlServerQueueProvider _qb; - private readonly ITestOutputHelper _console; private static ServiceProvider _serviceProvider; public SqlServerQueueProviderFixture(ITestOutputHelper output, SqlDockerSetup setup) { - _console = output; + Console = output; var connectionString = SqlDockerSetup.ConnectionString; ConfigureServices(); var opt = new SqlServerQueueProviderOption(connectionString, "UnitTest", true, true); - _qb = new SqlServerQueueProvider(_serviceProvider, opt); - _qb.Start().Wait(); + QueueProvider = new SqlServerQueueProvider(_serviceProvider, opt); + QueueProvider.Start().Wait(); - while (_qb.DequeueWork(QueueType.Event, CancellationToken.None).Result != null) - { - // Empty queue before test - } - - while (_qb.DequeueWork(QueueType.Workflow, CancellationToken.None).Result != null) - { - // Empty queue before test - } + Setup(); } private static void ConfigureServices() @@ -61,98 +41,7 @@ private static void ConfigureServices() public void Dispose() { - _qb.Dispose(); - } - - #endregion - - #region QueueDeque - - [Fact] - public void ShouldEnqueueAndDequeueAMessage() - { - var id = Guid.NewGuid().ToString(); - - DoTest(id, QueueType.Event); - - id = Guid.NewGuid().ToString(); - DoTest(id, QueueType.Workflow); - } - - void DoTest(string id, QueueType queueType) - { - _qb.QueueWork(id, queueType).Wait(); - var res = _qb.DequeueWork(queueType, CancellationToken.None).Result; - - res.Should().Be(id); - } - - #endregion - - [Fact] - public void ShouldEnqueueAndDequeueManyMessageOnManyThread() - { - const int countEvent = 250; - const int countThread = 10; - const QueueType queueType = QueueType.Event; - - var guids = new ConcurrentDictionary(); - - - bool stop = false; - var sw = Stopwatch.StartNew(); - - _console.WriteLine("Start dequeue task"); - var thDeque = new List(); - for (int i = 0; i < countThread; i++) - { - Task t = Task.Factory.StartNew(() => - { - _console.WriteLine("-> Dequeue task " + Task.CurrentId); - while (!stop) - { - var id = _qb.DequeueWork(queueType, CancellationToken.None).Result; - if (id != null) guids.AddOrUpdate(id, 0, (key, oldval) => oldval + 1); - } - - _console.WriteLine("<- Dequeue task " + Task.CurrentId); - }); - thDeque.Add(t); - } - - _console.WriteLine("Start enqueue task"); - var thEnque = new List(); - for (int i = 0; i < countThread; i++) - { - Task t = Task.Factory.StartNew(() => - { - _console.WriteLine("-> Enqueue task " + Task.CurrentId); - - for (int j = 0; j < countEvent; j++) - { - var guid = Guid.NewGuid().ToString(); - guids.TryAdd(guid, 0); - _qb.QueueWork(guid, queueType).Wait(); - } - - _console.WriteLine("<- Enqueue task " + Task.CurrentId); - }); - thEnque.Add(t); - } - - Task.WaitAll(thEnque.ToArray()); - _console.WriteLine("Enqueue complete " + sw.ElapsedMilliseconds + " msec"); - - stop = true; - Task.WaitAll(thDeque.ToArray()); - _console.WriteLine("Dequeue complete " + sw.ElapsedMilliseconds + " msec"); - - foreach (var guid in guids) - { - guid.Value.Should().Be(1); - } - - _console.WriteLine("MultiTest complete " + (guids.Count/(sw.ElapsedMilliseconds/1000.0)) + " msg/sec"); + QueueProvider.Dispose(); } } } \ No newline at end of file diff --git a/test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs b/test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs new file mode 100644 index 000000000..15739e2f6 --- /dev/null +++ b/test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs @@ -0,0 +1,154 @@ +#region using + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using FluentAssertions; + +using WorkflowCore.Interface; + +using Xunit; +using Xunit.Abstractions; + +#endregion + +namespace WorkflowCore.UnitTests +{ + public class BaseQueueProviderFixture + { + protected IQueueProvider QueueProvider; + protected ITestOutputHelper Console; + private bool _stop; + + #region Setup + + protected void Setup() + { + while (QueueProvider.DequeueWork(QueueType.Event, CancellationToken.None).Result != null) + { + // Empty queue before test + } + + while (QueueProvider.DequeueWork(QueueType.Workflow, CancellationToken.None).Result != null) + { + // Empty queue before test + } + } + + #endregion + + #region ShouldEnqueueAndDequeueAMessage + + [Fact] + public void ShouldEnqueueAndDequeueAMessage() + { + var id = Guid.NewGuid().ToString(); + + DoTest(id, QueueType.Event); + + id = Guid.NewGuid().ToString(); + DoTest(id, QueueType.Workflow); + } + + private void DoTest(string id, QueueType queueType) + { + QueueProvider.QueueWork(id, queueType).Wait(); + var res = QueueProvider.DequeueWork(queueType, CancellationToken.None).Result; + + res.Should().Be(id); + } + + #endregion + + #region ShouldEnqueueAndDequeueManyMessageOnManyThread + + [Fact] + public void ShouldEnqueueAndDequeueManyMessageOnManyThread() + { + const int countEvent = 250; + const int countThread = 10; + const QueueType queueType = QueueType.Event; + + var guids = new ConcurrentDictionary(); + + _stop = false; + var sw = Stopwatch.StartNew(); + + var thDeque = StartDequeueTask(countThread, queueType, guids); + var thEnque = StartEnqueueTask(countThread, countEvent, guids, queueType); + + Task.WaitAll(thEnque.ToArray()); + Console.WriteLine("Enqueue complete " + sw.ElapsedMilliseconds + " msec"); + + _stop = true; + Task.WaitAll(thDeque.ToArray()); + Console.WriteLine("Dequeue complete " + sw.ElapsedMilliseconds + " msec"); + + foreach (var guid in guids) + { + guid.Value.Should().Be(1); + } + + Console.WriteLine("Complete " + (guids.Count / (sw.ElapsedMilliseconds / 1000.0)) + " msg/sec"); + } + + private List StartEnqueueTask(int countThread, int countEvent, ConcurrentDictionary guids, QueueType queueType) + { + Console.WriteLine("Start enqueue task"); + + var thEnque = new List(); + for (int i = 0; i < countThread; i++) + { + Task t = Task.Factory.StartNew(() => + { + Console.WriteLine("-> Enqueue task " + Task.CurrentId); + + for (int j = 0; j < countEvent; j++) + { + var guid = Guid.NewGuid().ToString(); + guids.TryAdd(guid, 0); + QueueProvider.QueueWork(guid, queueType).Wait(); + } + + Console.WriteLine("<- Enqueue task " + Task.CurrentId); + }); + thEnque.Add(t); + } + + return thEnque; + } + + private List StartDequeueTask(int countThread, QueueType queueType, ConcurrentDictionary guids) + { + Console.WriteLine("Start dequeue task"); + + var thDeque = new List(); + for (int i = 0; i < countThread; i++) + { + Task t = Task.Factory.StartNew(() => + { + Console.WriteLine("-> Dequeue task " + Task.CurrentId); + while (!_stop) + { + var id = QueueProvider.DequeueWork(queueType, CancellationToken.None).Result; + if (id != null) guids.AddOrUpdate(id, 0, (key, oldval) => oldval + 1); + } + + Console.WriteLine("<- Dequeue task " + Task.CurrentId); + }); + thDeque.Add(t); + } + + return thDeque; + } + + #endregion + + + } +} \ No newline at end of file From ecfeffed0c37f110670597410bf49dcdf24b55c7 Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Tue, 6 Feb 2018 08:43:26 +0100 Subject: [PATCH 016/462] Fix migrator --- .../Services/SqlServerQueueProviderMigrator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index d4048d679..559e902e2 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -135,12 +135,12 @@ private void CreateMessageType(SqlConnection cn, SqlTransaction tx, string messa public void CreateDb() { - var pattern = ";Database=(.[^;]+);"; + var pattern = "Database=(.[^;]+);"; var regex = new Regex(pattern); var db = regex.Match(_connectionString).Groups[1].Value; - var masterCn = _connectionString.Replace(db, "master"); + var masterCn = _connectionString.Replace(regex.Match(_connectionString).Groups[0].Value, "Database=master;"); bool dbPresente; var cn = new SqlConnection(masterCn); From c653195ed13bfc34d4ea236b9be0d70bebeb2ccc Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Wed, 21 Feb 2018 16:21:14 +0100 Subject: [PATCH 017/462] Fix package reference in csproj Base class made abstract to resolte test error --- .../WorkflowCore.QueueProviders.SqlServer.csproj | 7 +------ test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 7b0f172e5..3e49c6812 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -17,16 +17,11 @@ + - - - ..\..\..\..\..\Users\r.paterlini\.nuget\packages\system.data.sqlclient\4.3.1\ref\netstandard1.3\System.Data.SqlClient.dll - - - diff --git a/test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs b/test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs index 15739e2f6..7d433ee2c 100644 --- a/test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs +++ b/test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs @@ -19,7 +19,7 @@ namespace WorkflowCore.UnitTests { - public class BaseQueueProviderFixture + public abstract class BaseQueueProviderFixture { protected IQueueProvider QueueProvider; protected ITestOutputHelper Console; From e781995bf63157590c8d54fce43ec03e1a5813bf Mon Sep 17 00:00:00 2001 From: Jackie Ja Date: Thu, 22 Feb 2018 13:46:23 +0800 Subject: [PATCH 018/462] Set workflow instance complete time correctly --- .../ExtensionMethods.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs index 9018bf73d..56420b344 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs @@ -131,7 +131,7 @@ internal static WorkflowInstance ToWorkflowInstance(this PersistedWorkflow insta result.WorkflowDefinitionId = instance.WorkflowDefinitionId; result.Status = instance.Status; result.CreateTime = DateTime.SpecifyKind(instance.CreateTime, DateTimeKind.Utc); - if (result.CompleteTime.HasValue) + if (instance.CompleteTime.HasValue) result.CompleteTime = DateTime.SpecifyKind(instance.CompleteTime.Value, DateTimeKind.Utc); foreach (var ep in instance.ExecutionPointers) From b437980bf67650c2f3347d33e673fa020b3dd18e Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 22 Feb 2018 09:02:56 +0100 Subject: [PATCH 019/462] Changed sql command to use parameters (where possible) --- .../Services/BrokerNamesProvider.cs | 16 ------------ .../Services/IBrokerNamesProvider.cs | 26 +++++++++++++++++++ .../Services/ISqlCommandExecutor.cs | 22 ++++++++++++++++ .../Services/QueueWork.sql | 11 +++----- .../Services/SqlCommandExecutor.cs | 12 --------- .../Services/SqlServerQueueProvider.cs | 18 +++++++++---- 6 files changed, 65 insertions(+), 40 deletions(-) create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs index e92072428..cfebceba2 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs @@ -7,22 +7,6 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { - /// - /// Base interface for - /// - public interface IBrokerNamesProvider { - string WorkflowContractName { get; } - string TargetEventServiceName { get; } - string InitiatorEventServiceName { get; } - string WorkflowQueueName { get; } - string EventQueueName { get; } - string TargetWorkflowServiceName { get; } - string InitiatorWorkflowServiceName { get; } - string EventContractName { get; } - string EventMessageType { get; } - string WorkflowMessageType { get; } - } - /// /// Build names for SSSB objects /// diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs new file mode 100644 index 000000000..307cc6377 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs @@ -0,0 +1,26 @@ +#region using + +using System; +using System.Linq; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer.Services +{ + /// + /// Base interface for + /// + public interface IBrokerNamesProvider + { + string WorkflowContractName { get; } + string TargetEventServiceName { get; } + string InitiatorEventServiceName { get; } + string WorkflowQueueName { get; } + string EventQueueName { get; } + string TargetWorkflowServiceName { get; } + string InitiatorWorkflowServiceName { get; } + string EventContractName { get; } + string EventMessageType { get; } + string WorkflowMessageType { get; } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs new file mode 100644 index 000000000..0815c14d0 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs @@ -0,0 +1,22 @@ +#region using + +using System; +using System.Data.SqlClient; +using System.Linq; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer.Services +{ + public interface ISqlCommandExecutor + { + /// + /// + /// + /// + /// + /// Add this value to parameter @name + /// + SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name = null); + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueWork.sql b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueWork.sql index d662fd3e4..82f176e93 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueWork.sql +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueWork.sql @@ -2,16 +2,13 @@ BEGIN TRAN BEGIN DIALOG @InitDlgHandle -FROM SERVICE -[{initiatorService}] -TO SERVICE -'{targetService}' -ON CONTRACT -[{contractName}] +FROM SERVICE @initiatorService +TO SERVICE @targetService +ON CONTRACT @contractName WITH ENCRYPTION=OFF; SEND ON CONVERSATION @InitDlgHandle -MESSAGE TYPE [{msgType}] +MESSAGE TYPE @msgType (@RequestMessage); COMMIT TRAN \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs index e05510cfa..db05e0db6 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs @@ -8,18 +8,6 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { - public interface ISqlCommandExecutor { - /// - /// - /// - /// - /// - /// - /// Add this value to parameter @name - /// - SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name = null); - } - public class SqlCommandExecutor : ISqlCommandExecutor { /// diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index df563e249..76b515e05 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -62,6 +62,8 @@ private static string GetFromResource(string file) public bool IsDequeueBlocking { get; } +#pragma warning disable CS1998 + public async Task Start() { if (_canCreateDb) _migrator.CreateDb(); @@ -73,6 +75,8 @@ public async Task Stop() // Do nothing } +#pragma warning restore CS1998 + public void Dispose() { Stop().Wait(); @@ -107,15 +111,19 @@ public async Task QueueWork(string id, QueueType queue) contractName = _names.EventContractName; } - var sql = _queueWork.Replace("{initiatorService}", initiatorService) - .Replace("{targetService}", targetService) - .Replace("{contractName}", contractName) - .Replace("{msgType}", msgType); + //var sql = _queueWork.Replace("{initiatorService}", initiatorService) + // .Replace("{targetService}", targetService) + // .Replace("{contractName}", contractName) + // .Replace("{msgType}", msgType); cn = new SqlConnection(_connectionString); cn.Open(); - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, null, sql)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, null, _queueWork)) { + cmd.Parameters.AddWithValue("@initiatorService", initiatorService); + cmd.Parameters.AddWithValue("@targetService", targetService); + cmd.Parameters.AddWithValue("@contractName", contractName); + cmd.Parameters.AddWithValue("@msgType", msgType); cmd.Parameters.AddWithValue("@RequestMessage", id); await cmd.ExecuteNonQueryAsync(); } From 3d5623ad6069511dbe6c21a7e6ba878cd3876728 Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Mon, 5 Mar 2018 11:44:16 +0100 Subject: [PATCH 020/462] Change usage of SqlCommandExecutor --- .../Services/ISqlCommandExecutor.cs | 3 +-- .../Services/SqlCommandExecutor.cs | 7 +------ .../Services/SqlServerQueueProvider.cs | 5 ----- .../SqlServerQueueProviderMigrator.cs | 20 ++++++++++++++----- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs index 0815c14d0..ec01aa08f 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs @@ -15,8 +15,7 @@ public interface ISqlCommandExecutor /// /// /// - /// Add this value to parameter @name /// - SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name = null); + SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext); } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs index db05e0db6..bde225a7c 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs @@ -16,17 +16,12 @@ public class SqlCommandExecutor : ISqlCommandExecutor /// /// /// - /// Add this value to parameter @name /// - public SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext, string name = null) + public SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext) { var cmd = cn.CreateCommand(); cmd.Transaction = tx; cmd.CommandText = cmdtext; - if (name != null) - { - cmd.Parameters.AddWithValue("name", name); - } return cmd; } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index 76b515e05..18d84ae95 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -111,11 +111,6 @@ public async Task QueueWork(string id, QueueType queue) contractName = _names.EventContractName; } - //var sql = _queueWork.Replace("{initiatorService}", initiatorService) - // .Replace("{targetService}", targetService) - // .Replace("{contractName}", contractName) - // .Replace("{msgType}", msgType); - cn = new SqlConnection(_connectionString); cn.Open(); using (var cmd = _sqlCommandExecutor.CreateCommand(cn, null, _queueWork)) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index 559e902e2..baa52c6cd 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -66,8 +66,10 @@ public void MigrateDb() private void CreateService(SqlConnection cn, SqlTransaction tx, string name, string queueName, string contractName) { var cmdtext = @"select name from sys.services where name=@name"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext, name)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) { + cmd.Parameters.AddWithValue("@name", name); + var n = (string)cmd.ExecuteScalar(); if (!String.IsNullOrEmpty(n)) return; @@ -83,8 +85,10 @@ private void CreateService(SqlConnection cn, SqlTransaction tx, string name, str private void CreateQueue(SqlConnection cn, SqlTransaction tx, string queueName) { var cmdtext = @"select name from sys.service_queues where name=@name"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext, queueName)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) { + cmd.Parameters.AddWithValue("@name", queueName); + var n = (string)cmd.ExecuteScalar(); if (!String.IsNullOrEmpty(n)) return; @@ -100,8 +104,10 @@ private void CreateQueue(SqlConnection cn, SqlTransaction tx, string queueName) private void CreateContract(SqlConnection cn, SqlTransaction tx, string contractName, string messageName) { var cmdtext = @"select name from sys.service_contracts where name=@name"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext, contractName)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) { + cmd.Parameters.AddWithValue("@name", contractName); + var n = (string)cmd.ExecuteScalar(); if (!String.IsNullOrEmpty(n)) return; @@ -117,8 +123,10 @@ private void CreateContract(SqlConnection cn, SqlTransaction tx, string contract private void CreateMessageType(SqlConnection cn, SqlTransaction tx, string message) { var cmdtext = @"select name from sys.service_message_types where name=@name"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext, message)) + using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) { + cmd.Parameters.AddWithValue("@name", message); + var n = (string)cmd.ExecuteScalar(); if (!String.IsNullOrEmpty(n)) return; @@ -186,7 +194,9 @@ private void EnableBroker(string masterCn, string db) var tx = cn.BeginTransaction(); var cmdtext = @"select is_broker_enabled from sys.databases where name = @name"; - var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext, db); + + var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext); + cmd.Parameters.AddWithValue("@name", db); bool isBrokerEnabled = (bool)cmd.ExecuteScalar(); if (isBrokerEnabled) return; From fd5bbd1655d7587831f2d29d84a1aae8f85d58ff Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Tue, 6 Mar 2018 15:17:26 +0100 Subject: [PATCH 021/462] Changed usage of BrokerNamesProvider --- .../Services/BrokerNamesProvider.cs | 74 ++++++++++++------- .../Services/IBrokerNamesProvider.cs | 12 +-- .../Services/SqlServerQueueProvider.cs | 31 +++----- .../SqlServerQueueProviderMigrator.cs | 30 ++++---- 4 files changed, 74 insertions(+), 73 deletions(-) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs index cfebceba2..51f3c52d5 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs @@ -2,11 +2,29 @@ using System; using System.Linq; +using WorkflowCore.Interface; #endregion namespace WorkflowCore.QueueProviders.SqlServer.Services { + public class BrokerNames + { + public BrokerNames(string msgType, string initiatorService, string targetService, string contractName, string queueName) + { + MsgType = msgType; + InitiatorService = initiatorService; + TargetService = targetService; + ContractName = contractName; + QueueName = queueName; + } + + public string MsgType { get; } + public string InitiatorService { get; } + public string TargetService { get; } + public string ContractName { get; } + public string QueueName { get; } + } /// /// Build names for SSSB objects /// @@ -15,46 +33,46 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services /// public class BrokerNamesProvider : IBrokerNamesProvider { + readonly BrokerNames _workFlowNames; + readonly BrokerNames _eventNames; + /// /// ctor /// /// public BrokerNamesProvider(string workflowHostName) { - WorkflowMessageType = "//workflow-core/workflow"; - EventMessageType = "//workflow-core/event"; + var workflowMessageType = "//workflow-core/workflow"; + var eventMessageType = "//workflow-core/event"; - EventContractName = "//workflow-core/eventContract"; - WorkflowContractName = "//workflow-core/workflowContract"; + var eventContractName = "//workflow-core/eventContract"; + var workflowContractName = "//workflow-core/workflowContract"; - InitiatorEventServiceName = $"//workflow-core/{workflowHostName}/initiatorEventService"; - TargetEventServiceName = $"//workflow-core/{workflowHostName}/targetEventService"; + var initiatorEventServiceName = $"//workflow-core/{workflowHostName}/initiatorEventService"; + var targetEventServiceName = $"//workflow-core/{workflowHostName}/targetEventService"; - InitiatorWorkflowServiceName = $"//workflow-core/{workflowHostName}/initiatorWorkflowService"; - TargetWorkflowServiceName = $"//workflow-core/{workflowHostName}/targetWorkflowService"; + var initiatorWorkflowServiceName = $"//workflow-core/{workflowHostName}/initiatorWorkflowService"; + var targetWorkflowServiceName = $"//workflow-core/{workflowHostName}/targetWorkflowService"; - EventQueueName = $"//workflow-core/{workflowHostName}/eventQueue"; - WorkflowQueueName = $"//workflow-core/{workflowHostName}/workflowQueue"; + var eventQueueName = $"//workflow-core/{workflowHostName}/eventQueue"; + var workflowQueueName = $"//workflow-core/{workflowHostName}/workflowQueue"; + + _workFlowNames = new BrokerNames(workflowMessageType, initiatorWorkflowServiceName, targetWorkflowServiceName, workflowContractName, eventQueueName); + _eventNames = new BrokerNames(eventMessageType, initiatorEventServiceName, targetEventServiceName, eventContractName, workflowQueueName); } - public string WorkflowContractName { get; } - - public string TargetEventServiceName { get; } - - public string InitiatorEventServiceName { get; } - - public string WorkflowQueueName { get; } - - public string EventQueueName { get; } - - public string TargetWorkflowServiceName { get; } - - public string InitiatorWorkflowServiceName { get; } - - public string EventContractName { get; } - - public string EventMessageType { get; } + public BrokerNames GetByQueue(QueueType queue) + { + switch (queue) + { + case QueueType.Workflow: + return _workFlowNames; + case QueueType.Event: + return _eventNames; + default: + throw new ArgumentOutOfRangeException(nameof(queue), queue, null); + } - public string WorkflowMessageType { get; } + } } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs index 307cc6377..5933592d3 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs @@ -2,6 +2,7 @@ using System; using System.Linq; +using WorkflowCore.Interface; #endregion @@ -12,15 +13,6 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services /// public interface IBrokerNamesProvider { - string WorkflowContractName { get; } - string TargetEventServiceName { get; } - string InitiatorEventServiceName { get; } - string WorkflowQueueName { get; } - string EventQueueName { get; } - string TargetWorkflowServiceName { get; } - string InitiatorWorkflowServiceName { get; } - string EventContractName { get; } - string EventMessageType { get; } - string WorkflowMessageType { get; } + BrokerNames GetByQueue(QueueType queue); } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index 18d84ae95..1e57a6aa6 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -96,29 +96,16 @@ public async Task QueueWork(string id, QueueType queue) SqlConnection cn = null; try { - string msgType, initiatorService, targetService, contractName; - if (queue == QueueType.Workflow) - { - msgType = _names.WorkflowMessageType; - initiatorService = _names.InitiatorWorkflowServiceName; - targetService = _names.TargetWorkflowServiceName; - contractName = _names.WorkflowContractName; - } else - { - msgType = _names.EventMessageType; - initiatorService = _names.InitiatorEventServiceName; - targetService = _names.TargetEventServiceName; - contractName = _names.EventContractName; - } - + var par = _names.GetByQueue(queue); + cn = new SqlConnection(_connectionString); cn.Open(); using (var cmd = _sqlCommandExecutor.CreateCommand(cn, null, _queueWork)) { - cmd.Parameters.AddWithValue("@initiatorService", initiatorService); - cmd.Parameters.AddWithValue("@targetService", targetService); - cmd.Parameters.AddWithValue("@contractName", contractName); - cmd.Parameters.AddWithValue("@msgType", msgType); + cmd.Parameters.AddWithValue("@initiatorService", par.InitiatorService); + cmd.Parameters.AddWithValue("@targetService", par.TargetService); + cmd.Parameters.AddWithValue("@contractName", par.ContractName); + cmd.Parameters.AddWithValue("@msgType", par.MsgType); cmd.Parameters.AddWithValue("@RequestMessage", id); await cmd.ExecuteNonQueryAsync(); } @@ -140,9 +127,9 @@ public async Task DequeueWork(QueueType queue, CancellationToken cancell SqlConnection cn = null; try { - var queueName = queue == QueueType.Workflow ? _names.WorkflowQueueName : _names.EventQueueName; - - var sql = _dequeueWork.Replace("{queueName}", queueName); + var par = _names.GetByQueue(queue); + + var sql = _dequeueWork.Replace("{queueName}", par.QueueName); cn = new SqlConnection(_connectionString); cn.Open(); diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index baa52c6cd..aa59a2ca5 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -4,6 +4,7 @@ using System.Data.SqlClient; using System.Linq; using System.Text.RegularExpressions; +using WorkflowCore.Interface; #endregion @@ -17,9 +18,9 @@ public interface ISqlServerQueueProviderMigrator public class SqlServerQueueProviderMigrator : ISqlServerQueueProviderMigrator { - readonly string _connectionString; + private readonly string _connectionString; - readonly IBrokerNamesProvider _names; + private readonly IBrokerNamesProvider _names; private readonly ISqlCommandExecutor _sqlCommandExecutor; public SqlServerQueueProviderMigrator(string connectionString, IBrokerNamesProvider names, ISqlCommandExecutor sqlCommandExecutor) @@ -41,21 +42,24 @@ public void MigrateDb() cn.Open(); var tx = cn.BeginTransaction(); - CreateMessageType(cn, tx, _names.WorkflowMessageType); - CreateMessageType(cn, tx, _names.EventMessageType); - - CreateContract(cn, tx, _names.EventContractName, _names.EventMessageType); - CreateContract(cn, tx, _names.WorkflowContractName, _names.WorkflowMessageType); + var n = new[] + { + _names.GetByQueue(QueueType.Workflow), + _names.GetByQueue(QueueType.Event) + }; - CreateQueue(cn, tx, _names.EventQueueName); - CreateQueue(cn, tx, _names.WorkflowQueueName); + foreach (var item in n) + { + CreateMessageType(cn, tx, item.MsgType); - CreateService(cn, tx, _names.InitiatorEventServiceName, _names.EventQueueName, _names.EventContractName); - CreateService(cn, tx, _names.TargetEventServiceName, _names.EventQueueName, _names.EventContractName); + CreateContract(cn, tx, item.ContractName, item.MsgType); - CreateService(cn, tx, _names.InitiatorWorkflowServiceName, _names.WorkflowQueueName, _names.WorkflowContractName); - CreateService(cn, tx, _names.TargetWorkflowServiceName, _names.WorkflowQueueName, _names.WorkflowContractName); + CreateQueue(cn, tx, item.QueueName); + CreateService(cn, tx, item.InitiatorService, item.QueueName, item.ContractName); + CreateService(cn, tx, item.TargetService, item.QueueName, item.ContractName); + } + tx.Commit(); } finally { From 3c819369af626dbc2da51da142d259a8e5346235 Mon Sep 17 00:00:00 2001 From: Roberto Paterlini Date: Thu, 8 Mar 2018 14:19:51 +0100 Subject: [PATCH 022/462] Change ctor of SqlServerProvider to remove direct reference to IServiceProvider --- .../ServiceCollectionExtensions.cs | 28 +++++++++---------- .../Services/SqlServerQueueProvider.cs | 14 +++------- .../SqlServerQueueProviderMigrator.cs | 1 + .../Services/SqlServerQueueProviderOption.cs | 25 ----------------- .../SqlServerQueueProviderOption.cs | 22 +++++++++++++++ .../SqlServerQueueProviderFixture.cs | 27 +++++++++--------- 6 files changed, 54 insertions(+), 63 deletions(-) delete mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderOption.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOption.cs diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index f2c1c1deb..20a6c6340 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -14,36 +14,36 @@ namespace Microsoft.Extensions.DependencyInjection public static class ServiceCollectionExtensions { /// - /// Use SQL Server as a queue provider + /// Use SQL Server as a queue provider /// /// - /// - /// - /// Autogenerate required service broker objects - /// + /// /// - public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, string connectionString, string workflowHostName, - bool canMigrateDb = false, bool canCreateDb = false) + public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, SqlServerQueueProviderOption opt) { options.UseQueueProvider(sp => { - var opt = new SqlServerQueueProviderOption(connectionString, workflowHostName, canMigrateDb, canCreateDb); - return new SqlServerQueueProvider(sp,opt); + var names = sp.GetService() + ?? new BrokerNamesProvider(opt.WorkflowHostName); + var sqlCommandExecutor = sp.GetService() + ?? new SqlCommandExecutor(); + var migrator = sp.GetService() + ?? new SqlServerQueueProviderMigrator(opt.ConnectionString, names, sqlCommandExecutor); + + return new SqlServerQueueProvider(opt, names, migrator, sqlCommandExecutor); }); return options; } /// - /// Use SQL Server as a queue provider (use 'default' as workflowHostName) + /// Use SQL Server as a queue provider (use 'default' as workflowHostName) /// /// /// - /// Autogenerate required service broker objects - /// /// - public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, string connectionString, bool canMigrateDb = false, bool canCreateDb = false) + public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, string connectionString) { - UseSqlServerBroker(options, connectionString, "default", canMigrateDb, canCreateDb); + UseSqlServerBroker(options, new SqlServerQueueProviderOption {ConnectionString = connectionString}); return options; } } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index 1e57a6aa6..e1f2f64da 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -8,8 +8,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; - using WorkflowCore.Interface; #endregion @@ -30,19 +28,15 @@ public class SqlServerQueueProvider : IQueueProvider private readonly string _queueWork; private readonly string _dequeueWork; - public SqlServerQueueProvider(IServiceProvider serviceProvider, SqlServerQueueProviderOption opt) + public SqlServerQueueProvider(SqlServerQueueProviderOption opt, IBrokerNamesProvider names, ISqlServerQueueProviderMigrator migrator, ISqlCommandExecutor sqlCommandExecutor) { + _names = names; + _migrator = migrator; + _sqlCommandExecutor = sqlCommandExecutor; _connectionString = opt.ConnectionString; _canMigrateDb = opt.CanMigrateDb; _canCreateDb = opt.CanCreateDb; - _names = serviceProvider.GetService() - ?? new BrokerNamesProvider(opt.WorkflowHostName); - _sqlCommandExecutor = serviceProvider.GetService() - ?? new SqlCommandExecutor(); - _migrator = serviceProvider.GetService() - ?? new SqlServerQueueProviderMigrator(opt.ConnectionString, _names, _sqlCommandExecutor); - IsDequeueBlocking = true; _queueWork = GetFromResource("QueueWork"); diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index aa59a2ca5..ac6a17c63 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -4,6 +4,7 @@ using System.Data.SqlClient; using System.Linq; using System.Text.RegularExpressions; + using WorkflowCore.Interface; #endregion diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderOption.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderOption.cs deleted file mode 100644 index a5fce583d..000000000 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderOption.cs +++ /dev/null @@ -1,25 +0,0 @@ -#region using - -using System; -using System.Linq; - -#endregion - -namespace WorkflowCore.QueueProviders.SqlServer.Services -{ - public class SqlServerQueueProviderOption - { - public SqlServerQueueProviderOption(string connectionString, string workflowHostName, bool canMigrateDb, bool canCreateDb) - { - ConnectionString = connectionString; - WorkflowHostName = workflowHostName; - CanMigrateDb = canMigrateDb; - CanCreateDb = canCreateDb; - } - - public string ConnectionString { get; private set; } - public string WorkflowHostName { get; private set; } - public bool CanMigrateDb { get; private set; } - public bool CanCreateDb { get; private set; } - } -} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOption.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOption.cs new file mode 100644 index 000000000..64da0aa12 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOption.cs @@ -0,0 +1,22 @@ +#region using + +using System; +using System.Linq; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer +{ + public class SqlServerQueueProviderOption + { + public SqlServerQueueProviderOption() + { + WorkflowHostName = "default"; + } + + public string ConnectionString { get; set; } + public string WorkflowHostName { get; set; } + public bool CanMigrateDb { get; set; } + public bool CanCreateDb { get; set; } + } +} \ No newline at end of file diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs index 5483d6782..468a79d63 100644 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs @@ -3,8 +3,7 @@ using System; using System.Linq; -using Microsoft.Extensions.DependencyInjection; - +using WorkflowCore.QueueProviders.SqlServer; using WorkflowCore.QueueProviders.SqlServer.Services; using WorkflowCore.UnitTests; @@ -18,27 +17,27 @@ namespace WorkflowCore.Tests.SqlServer [Collection("SqlServer collection")] public class SqlServerQueueProviderFixture : BaseQueueProviderFixture, IDisposable { - private static ServiceProvider _serviceProvider; - public SqlServerQueueProviderFixture(ITestOutputHelper output, SqlDockerSetup setup) { Console = output; var connectionString = SqlDockerSetup.ConnectionString; - ConfigureServices(); - - var opt = new SqlServerQueueProviderOption(connectionString, "UnitTest", true, true); - QueueProvider = new SqlServerQueueProvider(_serviceProvider, opt); + + var opt = new SqlServerQueueProviderOption { + ConnectionString = connectionString, + WorkflowHostName = "UnitTest", + CanCreateDb = true, + CanMigrateDb = true + }; + var names = new BrokerNamesProvider(opt.WorkflowHostName); + var sqlCommandExecutor = new SqlCommandExecutor(); + var migrator = new SqlServerQueueProviderMigrator(opt.ConnectionString, names, sqlCommandExecutor); + + QueueProvider = new SqlServerQueueProvider(opt,names,migrator,sqlCommandExecutor); QueueProvider.Start().Wait(); Setup(); } - private static void ConfigureServices() - { - IServiceCollection services = new ServiceCollection(); - _serviceProvider = services.BuildServiceProvider(); - } - public void Dispose() { QueueProvider.Dispose(); From a03341646fea873c75a23d652deb80cd4d95a49a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 8 Mar 2018 11:03:41 -0800 Subject: [PATCH 023/462] bump version --- .../WorkflowCore.Users/WorkflowCore.Users.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 59dfd0151..910ec2704 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -2,7 +2,7 @@ Workflow Core extensions for human workflow - 1.1.0 + 1.6.0 Daniel Gerlag netstandard1.3 WorkflowCore.Users @@ -18,9 +18,9 @@ false false Provides extensions for Workflow Core to enable human workflows. - 1.3.0 - 1.3.0.0 - 1.3.0.0 + 1.6.0 + 1.6.0.0 + 1.6.0.0 From 962c272d982fb30daff34ab87f2ea83677f8d101 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 8 Mar 2018 11:07:54 -0800 Subject: [PATCH 024/462] bump versions --- src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj index 325e4012f..baeef65f8 100644 --- a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj +++ b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj @@ -2,7 +2,7 @@ Workflow Core REST API - 1.1.0 + 1.6.0 Daniel Gerlag netstandard1.6 WorkflowCore.WebAPI @@ -17,7 +17,7 @@ false false false - 1.3.0 + 1.6.0 WebAPI wrapper for Workflow host From da68d518fedb287cfde3c5dadc6932c1a7e6ac3a Mon Sep 17 00:00:00 2001 From: Jackie Ja Date: Fri, 9 Mar 2018 16:19:24 +0800 Subject: [PATCH 025/462] Bump versions --- .../WorkflowCore.Persistence.EntityFramework.csproj | 6 +++--- .../WorkflowCore.Persistence.PostgreSQL.csproj | 6 +++--- .../WorkflowCore.Persistence.SqlServer.csproj | 6 +++--- .../WorkflowCore.Persistence.Sqlite.csproj | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 851918adb..19f507742 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -15,10 +15,10 @@ false false false - 1.6.0 + 1.6.1 Base package for Workflow-core peristence providers using entity framework - 1.6.0.0 - 1.6.0.0 + 1.6.1.0 + 1.6.1.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 6dcd7f3b2..dcd94315b 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 1.6.0 - 1.6.0.0 - 1.6.0.0 + 1.6.1 + 1.6.1.0 + 1.6.1.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 717973bbf..6b6d81193 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,10 +15,10 @@ false false false - 1.6.0 + 1.6.1 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 1.6.0.0 - 1.6.0.0 + 1.6.1.0 + 1.6.1.0 diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 51964723b..95f6bdbd5 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 1.6.0 - 1.6.0.0 - 1.6.0.0 + 1.6.1 + 1.6.1.0 + 1.6.1.0 From 2b19a8628346c813f14db3c487544283fadaad43 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 1 Apr 2018 18:28:13 -0700 Subject: [PATCH 026/462] Update README.md --- src/samples/WorkflowCore.Sample17/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/samples/WorkflowCore.Sample17/README.md b/src/samples/WorkflowCore.Sample17/README.md index 1926e829e..1481688ac 100644 --- a/src/samples/WorkflowCore.Sample17/README.md +++ b/src/samples/WorkflowCore.Sample17/README.md @@ -15,7 +15,7 @@ builder .Then() .CompensateWith() ) - .OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5)) + .OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5)) .Then(context => Console.WriteLine("End")); ``` @@ -34,7 +34,7 @@ builder .Then() .CompensateWith() ) - .CompensateWith() + .CompensateWith() .Then(context => Console.WriteLine("End")); ``` @@ -50,7 +50,7 @@ builder .Then() .Then() ) - .CompensateWith() + .CompensateWith() .Then(context => Console.WriteLine("End")); ``` @@ -126,4 +126,4 @@ The compensation steps can be defined by specifying the `CompensateWith` paramet } ] } -``` \ No newline at end of file +``` From b48bfd40b4105b3b02996c885a8b4c4397ac1594 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 12 Apr 2018 18:14:35 -0700 Subject: [PATCH 027/462] tests --- .../Interface/IExecutionPointerFactory.cs | 2 +- src/WorkflowCore/Models/WorkflowStep.cs | 20 +- .../Services/ExecutionPointerFactory.cs | 2 +- .../Services/WorkflowController.cs | 7 +- .../DistributedLockProviderTests.cs | 1 - .../BasePersistenceFixture.cs | 4 +- .../ExecutionResultProcessorFixture.cs | 202 ++++++++++ .../Services/WorkflowExecutorFixture.cs | 369 +++++++++++++----- 8 files changed, 494 insertions(+), 113 deletions(-) create mode 100644 test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs diff --git a/src/WorkflowCore/Interface/IExecutionPointerFactory.cs b/src/WorkflowCore/Interface/IExecutionPointerFactory.cs index f224dc43e..32a020eae 100644 --- a/src/WorkflowCore/Interface/IExecutionPointerFactory.cs +++ b/src/WorkflowCore/Interface/IExecutionPointerFactory.cs @@ -4,7 +4,7 @@ namespace WorkflowCore.Interface { public interface IExecutionPointerFactory { - ExecutionPointer BuildStartingPointer(WorkflowDefinition def); + ExecutionPointer BuildGenesisPointer(WorkflowDefinition def); ExecutionPointer BuildCompensationPointer(WorkflowDefinition def, ExecutionPointer pointer, ExecutionPointer exceptionPointer, int compensationStepId); ExecutionPointer BuildNextPointer(WorkflowDefinition def, ExecutionPointer pointer, StepOutcome outcomeTarget); ExecutionPointer BuildChildPointer(WorkflowDefinition def, ExecutionPointer pointer, int childDefinitionId, object branch); diff --git a/src/WorkflowCore/Models/WorkflowStep.cs b/src/WorkflowCore/Models/WorkflowStep.cs index 6d9bb012a..73ddb0561 100644 --- a/src/WorkflowCore/Models/WorkflowStep.cs +++ b/src/WorkflowCore/Models/WorkflowStep.cs @@ -9,25 +9,25 @@ public abstract class WorkflowStep { public abstract Type BodyType { get; } - public int Id { get; set; } + public virtual int Id { get; set; } - public string Name { get; set; } + public virtual string Name { get; set; } - public string Tag { get; set; } + public virtual string Tag { get; set; } - public List Children { get; set; } = new List(); + public virtual List Children { get; set; } = new List(); - public List Outcomes { get; set; } = new List(); + public virtual List Outcomes { get; set; } = new List(); - public List Inputs { get; set; } = new List(); + public virtual List Inputs { get; set; } = new List(); - public List Outputs { get; set; } = new List(); + public virtual List Outputs { get; set; } = new List(); - public WorkflowErrorHandling? ErrorBehavior { get; set; } + public virtual WorkflowErrorHandling? ErrorBehavior { get; set; } - public TimeSpan? RetryInterval { get; set; } + public virtual TimeSpan? RetryInterval { get; set; } - public int? CompensationStepId { get; set; } + public virtual int? CompensationStepId { get; set; } public virtual bool ResumeChildrenAfterCompensation => true; diff --git a/src/WorkflowCore/Services/ExecutionPointerFactory.cs b/src/WorkflowCore/Services/ExecutionPointerFactory.cs index ae74b6821..c98739736 100644 --- a/src/WorkflowCore/Services/ExecutionPointerFactory.cs +++ b/src/WorkflowCore/Services/ExecutionPointerFactory.cs @@ -10,7 +10,7 @@ namespace WorkflowCore.Services public class ExecutionPointerFactory : IExecutionPointerFactory { - public ExecutionPointer BuildStartingPointer(WorkflowDefinition def) + public ExecutionPointer BuildGenesisPointer(WorkflowDefinition def) { return new ExecutionPointer { diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index c9f68ccdf..5a124728f 100644 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -18,8 +18,7 @@ public class WorkflowController : IWorkflowController private readonly IQueueProvider _queueProvider; private readonly IExecutionPointerFactory _pointerFactory; private readonly ILogger _logger; - - + public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILoggerFactory loggerFactory) { _persistenceStore = persistenceStore; @@ -72,7 +71,7 @@ public async Task StartWorkflow(string workflowId, int? version, wf.Data = TypeExtensions.GetConstructor(def.DataType, new Type[] { }).Invoke(null); } - wf.ExecutionPointers.Add(_pointerFactory.BuildStartingPointer(def)); + wf.ExecutionPointers.Add(_pointerFactory.BuildGenesisPointer(def)); string id = await _persistenceStore.CreateNewWorkflow(wf); await _queueProvider.QueueWork(id, QueueType.Workflow); @@ -148,8 +147,6 @@ public async Task ResumeWorkflow(string workflowId) if (requeue) await _queueProvider.QueueWork(workflowId, QueueType.Workflow); } - - return false; } public async Task TerminateWorkflow(string workflowId) diff --git a/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs b/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs index 29f0a8f7e..7f1eebf1d 100644 --- a/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs +++ b/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs @@ -3,7 +3,6 @@ using System.Text; using System.Threading; using WorkflowCore.Interface; -using NUnit; using FluentAssertions; using NUnit.Framework; diff --git a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs index a315eba59..0b100ce55 100644 --- a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs +++ b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs @@ -15,7 +15,7 @@ public abstract class BasePersistenceFixture protected abstract IPersistenceProvider Subject { get; } [Fact] - public void CreateNewWorkflow() + public void CreateNewWorkflow_should_generate_id() { var workflow = new WorkflowInstance() { @@ -40,7 +40,7 @@ public void CreateNewWorkflow() } [Fact] - public void GetWorkflowInstance() + public void GetWorkflowInstance_should_retrieve_workflow() { var workflow = new WorkflowInstance() { diff --git a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs new file mode 100644 index 000000000..d381be03a --- /dev/null +++ b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs @@ -0,0 +1,202 @@ +using FakeItEasy; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Services; +using FluentAssertions; +using Xunit; +using WorkflowCore.Primitives; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace WorkflowCore.UnitTests.Services +{ + public class ExecutionResultProcessorFixture + { + + protected IExecutionResultProcessor Subject; + protected IExecutionPointerFactory PointerFactory; + protected IDateTimeProvider DateTimeProvider; + protected WorkflowOptions Options; + + public ExecutionResultProcessorFixture() + { + PointerFactory = A.Fake(); + DateTimeProvider = A.Fake(); + + Options = new WorkflowOptions(); + + A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); + + //config logging + var loggerFactory = new LoggerFactory(); + loggerFactory.AddConsole(LogLevel.Debug); + + Subject = new ExecutionResultProcessor(PointerFactory, DateTimeProvider, Options, loggerFactory); + } + + [Fact(DisplayName = "Should advance workflow")] + public void should_advance_workflow() + { + //arrange + var definition = new WorkflowDefinition(); + var pointer1 = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer2 = new ExecutionPointer(); + var outcome = new StepOutcome() { NextStep = 1 }; + var step = A.Fake(); + var workflowResult = new WorkflowExecutorResult(); + var instance = GivenWorkflow(pointer1); + var result = ExecutionResult.Next(); + + A.CallTo(() => step.Outcomes).Returns(new List() { outcome }); + A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome)).Returns(pointer2); + + //act + Subject.ProcessExecutionResult(instance, definition, pointer1, step, result, workflowResult); + + //assert + pointer1.Active.Should().BeFalse(); + pointer1.Status.Should().Be(PointerStatus.Complete); + pointer1.EndTime.Should().NotBeNull(); + + A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome)).MustHaveHappened(); + instance.ExecutionPointers.Should().Contain(pointer2); + } + + [Fact(DisplayName = "Should set persistence data")] + public void should_set_persistence_data() + { + //arrange + var persistenceData = new object(); + var definition = new WorkflowDefinition(); + var pointer = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; + var step = A.Fake(); + var workflowResult = new WorkflowExecutorResult(); + var instance = GivenWorkflow(pointer); + var result = ExecutionResult.Persist(persistenceData); + + //act + Subject.ProcessExecutionResult(instance, definition, pointer, step, result, workflowResult); + + //assert + pointer.PersistenceData.Should().Be(persistenceData); + } + + [Fact(DisplayName = "Should subscribe to event")] + public void should_subscribe_to_event() + { + //arrange + var definition = new WorkflowDefinition(); + var pointer = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; + var step = A.Fake(); + var workflowResult = new WorkflowExecutorResult(); + var instance = GivenWorkflow(pointer); + var result = ExecutionResult.WaitForEvent("Event", "Key", DateTime.Now); + + //act + Subject.ProcessExecutionResult(instance, definition, pointer, step, result, workflowResult); + + //assert + pointer.Status.Should().Be(PointerStatus.WaitingForEvent); + pointer.Active.Should().BeFalse(); + pointer.EventName.Should().Be("Event"); + pointer.EventKey.Should().Be("Key"); + workflowResult.Subscriptions.Should().Contain(x => x.StepId == pointer.StepId && x.EventName == "Event" && x.EventKey == "Key"); + } + + [Fact(DisplayName = "Should select correct outcomes")] + public void should_select_correct_outcomes() + { + //arrange + var definition = new WorkflowDefinition(); + var pointer1 = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer2 = new ExecutionPointer(); + var pointer3 = new ExecutionPointer(); + var outcome1 = new StepOutcome() { NextStep = 1, Value = data => 10 }; + var outcome2 = new StepOutcome() { NextStep = 2, Value = data => 20 }; + var step = A.Fake(); + var workflowResult = new WorkflowExecutorResult(); + var instance = GivenWorkflow(pointer1); + var result = ExecutionResult.Outcome(20); + + A.CallTo(() => step.Outcomes).Returns(new List() { outcome1, outcome2 }); + A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome1)).Returns(pointer2); + A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome2)).Returns(pointer3); + + //act + Subject.ProcessExecutionResult(instance, definition, pointer1, step, result, workflowResult); + + //assert + pointer1.Active.Should().BeFalse(); + pointer1.Status.Should().Be(PointerStatus.Complete); + pointer1.EndTime.Should().NotBeNull(); + + A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome1)).MustNotHaveHappened(); + A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome2)).MustHaveHappened(); + instance.ExecutionPointers.Should().NotContain(pointer2); + instance.ExecutionPointers.Should().Contain(pointer3); + } + + [Fact(DisplayName = "Should sleep pointer")] + public void should_sleep_pointer() + { + //arrange + var persistenceData = new object(); + var definition = new WorkflowDefinition(); + var pointer = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; + var step = A.Fake(); + var workflowResult = new WorkflowExecutorResult(); + var instance = GivenWorkflow(pointer); + var result = ExecutionResult.Sleep(TimeSpan.FromMinutes(5), persistenceData); + + //act + Subject.ProcessExecutionResult(instance, definition, pointer, step, result, workflowResult); + + //assert + pointer.Status.Should().Be(PointerStatus.Sleeping); + pointer.SleepUntil.Should().NotBeNull(); + } + + [Fact(DisplayName = "Should branch children")] + public void should_branch_children() + { + //arrange + var branch = 10; + var child = 2; + var definition = new WorkflowDefinition(); + var pointer = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; + var childPointer = new ExecutionPointer(); + var step = A.Fake(); + var workflowResult = new WorkflowExecutorResult(); + var instance = GivenWorkflow(pointer); + var result = ExecutionResult.Branch(new List() { branch }, null); + + A.CallTo(() => step.Children).Returns(new List() { child }); + A.CallTo(() => PointerFactory.BuildChildPointer(definition, pointer, child, branch)).Returns(childPointer); + + //act + Subject.ProcessExecutionResult(instance, definition, pointer, step, result, workflowResult); + + //assert + A.CallTo(() => PointerFactory.BuildChildPointer(definition, pointer, child, branch)).MustHaveHappened(); + instance.ExecutionPointers.Should().Contain(childPointer); + } + + + private static WorkflowInstance GivenWorkflow(ExecutionPointer pointer) + { + return new WorkflowInstance + { + Status = WorkflowStatus.Runnable, + ExecutionPointers = new List() + { + pointer + } + }; + } + } +} diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 3705152d3..dff65ac25 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -9,146 +9,329 @@ using WorkflowCore.Services; using FluentAssertions; using Xunit; +using WorkflowCore.Primitives; +using System.Linq.Expressions; +using System.Threading.Tasks; namespace WorkflowCore.UnitTests.Services { public class WorkflowExecutorFixture { - class EventSubscribeTestWorkflow : IWorkflow - { - static int StartStepTicker = 0; - public string Id { get { return "EventSubscribeTestWorkflow"; } } - public int Version { get { return 1; } } - public void Build(IWorkflowBuilder builder) - { - builder - .StartWith(context => - { - StartStepTicker++; - return ExecutionResult.Next(); - }) - .WaitFor("MyEvent", data => "0"); - } - - } - - class StepExecutionTestWorkflow : IWorkflow - { - public static int Step1StepTicker = 0; - public static int Step2StepTicker = 0; - public string Id { get { return "StepExecutionTestWorkflow"; } } - public int Version { get { return 1; } } - public void Build(IWorkflowBuilder builder) - { - builder - .StartWith(context => - { - Step1StepTicker++; - return ExecutionResult.Next(); - }) - .Then(context => - { - Step2StepTicker++; - return ExecutionResult.Next(); - }); - } - } - protected IWorkflowExecutor Subject; protected IWorkflowHost Host; protected IPersistenceProvider PersistenceProvider; protected IWorkflowRegistry Registry; protected IExecutionResultProcessor ResultProcesser; + protected IServiceProvider ServiceProvider; + protected IDateTimeProvider DateTimeProvider; protected WorkflowOptions Options; public WorkflowExecutorFixture() { - //setup dependency injection - IServiceCollection services = new ServiceCollection(); - services.AddLogging(); + Host = A.Fake(); + PersistenceProvider = A.Fake(); + ServiceProvider = A.Fake(); + Registry = A.Fake(); + ResultProcesser = A.Fake(); + DateTimeProvider = A.Fake(); - //TODO: mock these dependencies to make true unit tests Options = new WorkflowOptions(); - services.AddSingleton(Options); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - Host = A.Fake(); - PersistenceProvider = A.Fake(); - var serviceProvider = services.BuildServiceProvider(); + A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddConsole(LogLevel.Debug); + var loggerFactory = new LoggerFactory(); + loggerFactory.AddConsole(LogLevel.Debug); + + Subject = new WorkflowExecutor(Registry, ServiceProvider, DateTimeProvider, ResultProcesser, Options, loggerFactory); + } - Registry = serviceProvider.GetService(); - ResultProcesser = serviceProvider.GetService(); + [Fact(DisplayName = "Should execute active step")] + public void should_execute_active_step() + { + //arrange + var step1Body = A.Fake(); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); + WorkflowStep step1 = BuildFakeStep(step1Body); + Given1StepWorkflow(step1, "Workflow", 1); + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + ExecutionPointers = new List() + { + new ExecutionPointer() { Active = true, StepId = 0 } + } + }; + + //act + Subject.Execute(instance); - Subject = new WorkflowExecutor(Registry, serviceProvider, new DateTimeProvider(), ResultProcesser, Options, loggerFactory); + //assert + A.CallTo(() => step1Body.RunAsync(A.Ignored)).MustHaveHappened(); + A.CallTo(() => ResultProcesser.ProcessExecutionResult(instance, A.Ignored, A.Ignored, step1, A.Ignored, A.Ignored)).MustHaveHappened(); } - [Fact] - public void EventSubscribe() + [Fact(DisplayName = "Should trigger step hooks")] + public void should_trigger_step_hooks() { - //arrange - var def = new EventSubscribeTestWorkflow(); - Registry.RegisterWorkflow(def); + //arrange + var step1Body = A.Fake(); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); + WorkflowStep step1 = BuildFakeStep(step1Body); + Given1StepWorkflow(step1, "Workflow", 1); + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + ExecutionPointers = new List() + { + new ExecutionPointer() { Active = true, StepId = 0 } + } + }; - var instance = new WorkflowInstance(); - instance.WorkflowDefinitionId = def.Id; - instance.Version = def.Version; - instance.Status = WorkflowStatus.Runnable; - instance.NextExecution = 0; - instance.Id = "001"; + //act + Subject.Execute(instance); + + //assert + A.CallTo(() => step1.InitForExecution(A.Ignored, A.Ignored, A.Ignored, A.Ignored)).MustHaveHappened(); + A.CallTo(() => step1.BeforeExecute(A.Ignored, A.Ignored, A.Ignored, A.Ignored)).MustHaveHappened(); + A.CallTo(() => step1.AfterExecute(A.Ignored, A.Ignored, A.Ignored, A.Ignored)).MustHaveHappened(); + } - var executionPointer = new ExecutionPointer() + [Fact(DisplayName = "Should not execute inactive step")] + public void should_not_execute_inactive_step() + { + //arrange + var step1Body = A.Fake(); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); + WorkflowStep step1 = BuildFakeStep(step1Body); + Given1StepWorkflow(step1, "Workflow", 1); + + var instance = new WorkflowInstance { - Active = true, - StepId = 1 + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + ExecutionPointers = new List() + { + new ExecutionPointer() { Active = false, StepId = 0 } + } }; + + //act + Subject.Execute(instance); + + //assert + A.CallTo(() => step1Body.RunAsync(A.Ignored)).MustNotHaveHappened(); + } + + [Fact(DisplayName = "Should map inputs")] + public void should_map_inputs() + { + //arrange + Expression> p1 = x => x.Property1; + Expression> v1 = (x, context) => x.Value1; + + var step1Body = A.Fake(); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); + WorkflowStep step1 = BuildFakeStep(step1Body, new List() + { + new DataMapping() + { + Source = v1, + Target = p1 + } + } + , new List()); - instance.ExecutionPointers.Add(executionPointer); + Given1StepWorkflow(step1, "Workflow", 1); + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + Data = new DataClass() { Value1 = 5 }, + ExecutionPointers = new List() + { + new ExecutionPointer() { Active = true, StepId = 0 } + } + }; //act Subject.Execute(instance); //assert - executionPointer.EventName.Should().Be("MyEvent"); - executionPointer.EventKey.Should().Be("0"); - executionPointer.Active.Should().Be(false); + step1Body.Property1.Should().Be(5); } - [Fact] - public void StepExecution() + [Fact(DisplayName = "Should map outputs")] + public void should_map_outputs() { //arrange - var def = new StepExecutionTestWorkflow(); - Registry.RegisterWorkflow(def); + Expression> p1 = x => x.Property1; + Expression> v1 = (x, context) => x.Value1; + + var step1Body = A.Fake(); + A.CallTo(() => step1Body.Property1).Returns(7); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); + WorkflowStep step1 = BuildFakeStep(step1Body, new List(), new List() + { + new DataMapping() + { + Source = p1, + Target = v1 + } + } + ); + + Given1StepWorkflow(step1, "Workflow", 1); - var instance = new WorkflowInstance(); - instance.WorkflowDefinitionId = def.Id; - instance.Version = def.Version; - instance.Status = WorkflowStatus.Runnable; - instance.NextExecution = 0; - instance.Id = "001"; + var data = new DataClass() { Value1 = 5 }; - instance.ExecutionPointers.Add(new ExecutionPointer() + var instance = new WorkflowInstance { - Active = true, - StepId = 0 - }); + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + Data = data, + ExecutionPointers = new List() + { + new ExecutionPointer() { Active = true, StepId = 0 } + } + }; + + //act + Subject.Execute(instance); + + //assert + data.Value1.Should().Be(7); + } + + [Fact(DisplayName = "Should handle step exception")] + public void should_handle_step_exception() + { + //arrange + var step1Body = A.Fake(); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Throws(); + WorkflowStep step1 = BuildFakeStep(step1Body); + Given1StepWorkflow(step1, "Workflow", 1); + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + ExecutionPointers = new List() + { + new ExecutionPointer() { Active = true, StepId = 0 } + } + }; //act Subject.Execute(instance); + + //assert + A.CallTo(() => step1Body.RunAsync(A.Ignored)).MustHaveHappened(); + A.CallTo(() => ResultProcesser.HandleStepException(instance, A.Ignored, A.Ignored, step1)).MustHaveHappened(); + A.CallTo(() => ResultProcesser.ProcessExecutionResult(instance, A.Ignored, A.Ignored, step1, A.Ignored, A.Ignored)).MustNotHaveHappened(); + } + + [Fact(DisplayName = "Should process after execution iteration")] + public void should_process_after_execution_iteration() + { + //arrange + var step1Body = A.Fake(); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Persist(null)); + WorkflowStep step1 = BuildFakeStep(step1Body); + Given1StepWorkflow(step1, "Workflow", 1); + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + ExecutionPointers = new List() + { + new ExecutionPointer() { Active = true, StepId = 0 } + } + }; + + //act Subject.Execute(instance); //assert - StepExecutionTestWorkflow.Step1StepTicker.Should().Be(1); - StepExecutionTestWorkflow.Step2StepTicker.Should().Be(1); - instance.Status.Should().Be(WorkflowStatus.Complete); + A.CallTo(() => step1.AfterWorkflowIteration(A.Ignored, A.Ignored, instance, A.Ignored)).MustHaveHappened(); + } + + + private void Given1StepWorkflow(WorkflowStep step1, string id, int version) + { + A.CallTo(() => Registry.GetDefinition(id, version)).Returns(new WorkflowDefinition() + { + Id = id, + Version = version, + DataType = typeof(object), + Steps = new List() + { + step1 + } + + }); + } + + private WorkflowStep BuildFakeStep(IStepBody stepBody) + { + return BuildFakeStep(stepBody, new List(), new List()); + } + + private WorkflowStep BuildFakeStep(IStepBody stepBody, List inputs, List outputs) + { + var result = A.Fake(); + A.CallTo(() => result.Id).Returns(0); + A.CallTo(() => result.BodyType).Returns(stepBody.GetType()); + A.CallTo(() => result.ResumeChildrenAfterCompensation).Returns(true); + A.CallTo(() => result.RevertChildrenAfterCompensation).Returns(false); + A.CallTo(() => result.ConstructBody(ServiceProvider)).Returns(stepBody); + A.CallTo(() => result.Inputs).Returns(inputs); + A.CallTo(() => result.Outputs).Returns(outputs); + A.CallTo(() => result.Outcomes).Returns(new List()); + A.CallTo(() => result.InitForExecution(A.Ignored, A.Ignored, A.Ignored, A.Ignored)).Returns(ExecutionPipelineDirective.Next); + A.CallTo(() => result.BeforeExecute(A.Ignored, A.Ignored, A.Ignored, A.Ignored)).Returns(ExecutionPipelineDirective.Next); + return result; + } + + public interface IStepWithProperties : IStepBody + { + int Property1 { get; set; } + int Property2 { get; set; } + int Property3 { get; set; } + } + + public class DataClass + { + public int Value1 { get; set; } + public int Value2 { get; set; } + public int Value3 { get; set; } } } } From fcc8d9e5a199fcabb9f14dca859024f9f4b024f6 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 21 Apr 2018 18:17:40 -0700 Subject: [PATCH 028/462] improve code quality for Sql Server Broker provider --- src/WorkflowCore/Models/WorkflowOptions.cs | 10 +- .../ServiceCollectionExtensions.cs | 2 +- .../Interfaces/IQueueConfigProvider.cs | 16 ++ .../Interfaces/ISqlCommandExecutor.cs | 19 ++ .../ISqlServerQueueProviderMigrator.cs | 12 ++ .../Models/QueueConfig.cs | 24 +++ .../README.md | 4 +- .../ServiceCollectionExtensions.cs | 40 ++-- .../Services/BrokerNamesProvider.cs | 78 ------- .../Services/IBrokerNamesProvider.cs | 18 -- .../Services/ISqlCommandExecutor.cs | 21 -- .../Services/QueueConfigProvider.cs | 59 ++++++ .../Services/SqlCommandExecutor.cs | 41 +++- .../Services/SqlServerQueueProvider.cs | 76 ++++--- .../SqlServerQueueProviderMigrator.cs | 192 +++++++----------- .../{Services => SqlCommands}/DequeueWork.sql | 0 .../{Services => SqlCommands}/QueueWork.sql | 0 .../SqlServerQueueProviderOption.cs | 22 -- .../SqlServerQueueProviderOptions.cs | 16 ++ ...rkflowCore.QueueProviders.SqlServer.csproj | 9 +- .../SqlServerQueueProviderFixture.cs | 46 ----- .../BaseQueueProviderFixture.cs | 154 -------------- .../ExecutionResultProcessorFixture.cs | 2 +- .../Services/WorkflowExecutorFixture.cs | 2 +- 24 files changed, 320 insertions(+), 543 deletions(-) create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/IQueueConfigProvider.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlCommandExecutor.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs delete mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs delete mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs delete mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs rename src/providers/WorkflowCore.QueueProviders.SqlServer/{Services => SqlCommands}/DequeueWork.sql (100%) rename src/providers/WorkflowCore.QueueProviders.SqlServer/{Services => SqlCommands}/QueueWork.sql (100%) delete mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOption.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOptions.cs delete mode 100644 test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs delete mode 100644 test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index 71dcb8668..da0536a13 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Extensions.DependencyInjection; +using System; using WorkflowCore.Interface; using WorkflowCore.Services; @@ -11,10 +12,13 @@ public class WorkflowOptions internal Func LockFactory; internal TimeSpan PollInterval; internal TimeSpan IdleTime; - internal TimeSpan ErrorRetryInterval; + internal TimeSpan ErrorRetryInterval; - public WorkflowOptions() + public IServiceCollection Services { get; private set; } + + public WorkflowOptions(IServiceCollection services) { + Services = services; PollInterval = TimeSpan.FromSeconds(10); IdleTime = TimeSpan.FromMilliseconds(100); ErrorRetryInterval = TimeSpan.FromSeconds(60); diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 203ad535d..fd464823a 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -21,7 +21,7 @@ public static void AddWorkflow(this IServiceCollection services, Action x.ServiceType == typeof(WorkflowOptions))) throw new InvalidOperationException("Workflow services already registered"); - var options = new WorkflowOptions(); + var options = new WorkflowOptions(services); setupAction?.Invoke(options); services.AddTransient(options.PersistanceFactory); services.AddSingleton(options.QueueFactory); diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/IQueueConfigProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/IQueueConfigProvider.cs new file mode 100644 index 000000000..0f69f5355 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/IQueueConfigProvider.cs @@ -0,0 +1,16 @@ +#region using + +using System; +using System.Linq; +using WorkflowCore.Interface; +using WorkflowCore.QueueProviders.SqlServer.Models; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer.Interfaces +{ + public interface IQueueConfigProvider + { + QueueConfig GetByQueue(QueueType queue); + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlCommandExecutor.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlCommandExecutor.cs new file mode 100644 index 000000000..ea4d4aac0 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlCommandExecutor.cs @@ -0,0 +1,19 @@ +#region using + +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Data.SqlClient; +using System.Linq; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer.Interfaces +{ + public interface ISqlCommandExecutor + { + TResult ExecuteScalar(IDbConnection cn, IDbTransaction tx, string cmdtext, params DbParameter[] parameters); + int ExecuteCommand(IDbConnection cn, IDbTransaction tx, string cmdtext, params DbParameter[] parameters); + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs new file mode 100644 index 000000000..bbe916147 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.QueueProviders.SqlServer.Interfaces +{ + public interface ISqlServerQueueProviderMigrator + { + void MigrateDb(); + void CreateDb(); + } +} diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs new file mode 100644 index 000000000..1bef78367 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.QueueProviders.SqlServer.Models +{ + public class QueueConfig + { + public QueueConfig(string msgType, string initiatorService, string targetService, string contractName, string queueName) + { + MsgType = msgType; + InitiatorService = initiatorService; + TargetService = targetService; + ContractName = contractName; + QueueName = queueName; + } + + public string MsgType { get; } + public string InitiatorService { get; } + public string TargetService { get; } + public string ContractName { get; } + public string QueueName { get; } + } +} diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md index 3277962aa..47055c822 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md @@ -1,6 +1,6 @@ # SQL Server Service Broker queue provider for Workflow Core -Provides distributed worker support on [Workflow Core](../../../README.md) using [SQL Server Service Broker](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-service-broker). +Provides distributed worker support on [Workflow Core](../../../README.md) using [SQL Server Service Broker](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-service-broker). This makes it possible to have a cluster of nodes processing your workflows, along with a distributed lock manager. @@ -17,7 +17,7 @@ PM> Install-Package WorkflowCore.QueueProviders.SqlServer -Pre Use the .UseSqlServerQueue extension method when building your service provider. ```C# -services.AddWorkflow(x => x.UseSqlServerQueue(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canMigrateDB)); +services.AddWorkflow(x => x.UseSqlServerBroker(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canMigrateDB)); ``` diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index 20a6c6340..f3076c523 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -1,10 +1,11 @@ #region using using System; -using System.Linq; +using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Models; using WorkflowCore.QueueProviders.SqlServer; +using WorkflowCore.QueueProviders.SqlServer.Interfaces; using WorkflowCore.QueueProviders.SqlServer.Services; #endregion @@ -12,38 +13,31 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions - { + { /// /// Use SQL Server as a queue provider /// /// - /// + /// /// - public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, SqlServerQueueProviderOption opt) + public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, string connectionString, bool canCreateDb, bool canMigrateDb) { - options.UseQueueProvider(sp => + options.Services.AddTransient(); + options.Services.AddTransient(); + options.Services.AddTransient(); + + var sqlOptions = new SqlServerQueueProviderOptions() { - var names = sp.GetService() - ?? new BrokerNamesProvider(opt.WorkflowHostName); - var sqlCommandExecutor = sp.GetService() - ?? new SqlCommandExecutor(); - var migrator = sp.GetService() - ?? new SqlServerQueueProviderMigrator(opt.ConnectionString, names, sqlCommandExecutor); + ConnectionString = connectionString, + CanCreateDb = canCreateDb, + CanMigrateDb = canMigrateDb + }; - return new SqlServerQueueProvider(opt, names, migrator, sqlCommandExecutor); + options.UseQueueProvider(sp => + { + return new SqlServerQueueProvider(sqlOptions, sp.GetService(), sp.GetService(), sp.GetService()); }); - return options; - } - /// - /// Use SQL Server as a queue provider (use 'default' as workflowHostName) - /// - /// - /// - /// - public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, string connectionString) - { - UseSqlServerBroker(options, new SqlServerQueueProviderOption {ConnectionString = connectionString}); return options; } } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs deleted file mode 100644 index 51f3c52d5..000000000 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/BrokerNamesProvider.cs +++ /dev/null @@ -1,78 +0,0 @@ -#region using - -using System; -using System.Linq; -using WorkflowCore.Interface; - -#endregion - -namespace WorkflowCore.QueueProviders.SqlServer.Services -{ - public class BrokerNames - { - public BrokerNames(string msgType, string initiatorService, string targetService, string contractName, string queueName) - { - MsgType = msgType; - InitiatorService = initiatorService; - TargetService = targetService; - ContractName = contractName; - QueueName = queueName; - } - - public string MsgType { get; } - public string InitiatorService { get; } - public string TargetService { get; } - public string ContractName { get; } - public string QueueName { get; } - } - /// - /// Build names for SSSB objects - /// - /// - /// Message type and contract are global, service name and queue different for every workflow host - /// - public class BrokerNamesProvider : IBrokerNamesProvider - { - readonly BrokerNames _workFlowNames; - readonly BrokerNames _eventNames; - - /// - /// ctor - /// - /// - public BrokerNamesProvider(string workflowHostName) - { - var workflowMessageType = "//workflow-core/workflow"; - var eventMessageType = "//workflow-core/event"; - - var eventContractName = "//workflow-core/eventContract"; - var workflowContractName = "//workflow-core/workflowContract"; - - var initiatorEventServiceName = $"//workflow-core/{workflowHostName}/initiatorEventService"; - var targetEventServiceName = $"//workflow-core/{workflowHostName}/targetEventService"; - - var initiatorWorkflowServiceName = $"//workflow-core/{workflowHostName}/initiatorWorkflowService"; - var targetWorkflowServiceName = $"//workflow-core/{workflowHostName}/targetWorkflowService"; - - var eventQueueName = $"//workflow-core/{workflowHostName}/eventQueue"; - var workflowQueueName = $"//workflow-core/{workflowHostName}/workflowQueue"; - - _workFlowNames = new BrokerNames(workflowMessageType, initiatorWorkflowServiceName, targetWorkflowServiceName, workflowContractName, eventQueueName); - _eventNames = new BrokerNames(eventMessageType, initiatorEventServiceName, targetEventServiceName, eventContractName, workflowQueueName); - } - - public BrokerNames GetByQueue(QueueType queue) - { - switch (queue) - { - case QueueType.Workflow: - return _workFlowNames; - case QueueType.Event: - return _eventNames; - default: - throw new ArgumentOutOfRangeException(nameof(queue), queue, null); - } - - } - } -} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs deleted file mode 100644 index 5933592d3..000000000 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/IBrokerNamesProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -#region using - -using System; -using System.Linq; -using WorkflowCore.Interface; - -#endregion - -namespace WorkflowCore.QueueProviders.SqlServer.Services -{ - /// - /// Base interface for - /// - public interface IBrokerNamesProvider - { - BrokerNames GetByQueue(QueueType queue); - } -} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs deleted file mode 100644 index ec01aa08f..000000000 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/ISqlCommandExecutor.cs +++ /dev/null @@ -1,21 +0,0 @@ -#region using - -using System; -using System.Data.SqlClient; -using System.Linq; - -#endregion - -namespace WorkflowCore.QueueProviders.SqlServer.Services -{ - public interface ISqlCommandExecutor - { - /// - /// - /// - /// - /// - /// - SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext); - } -} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs new file mode 100644 index 000000000..4b7547a0b --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs @@ -0,0 +1,59 @@ +#region using + +using System; +using System.Linq; +using WorkflowCore.Interface; +using WorkflowCore.QueueProviders.SqlServer.Interfaces; +using WorkflowCore.QueueProviders.SqlServer.Models; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer.Services +{ + + /// + /// Build names for SSSB objects + /// + /// + /// Message type and contract are global, service name and queue different for every workflow host + /// + public class QueueConfigProvider : IQueueConfigProvider + { + private readonly QueueConfig _workflowQueueConfig; + private readonly QueueConfig _eventQueueConfig; + + public QueueConfigProvider() + { + var workflowMessageType = "//workflow-core/workflow"; + var eventMessageType = "//workflow-core/event"; + + var eventContractName = "//workflow-core/eventContract"; + var workflowContractName = "//workflow-core/workflowContract"; + + var initiatorEventServiceName = $"//workflow-core/initiatorEventService"; + var targetEventServiceName = $"//workflow-core/targetEventService"; + + var initiatorWorkflowServiceName = $"//workflow-core/initiatorWorkflowService"; + var targetWorkflowServiceName = $"//workflow-core/targetWorkflowService"; + + var eventQueueName = $"//workflow-core/eventQueue"; + var workflowQueueName = $"//workflow-core/workflowQueue"; + + _workflowQueueConfig = new QueueConfig(workflowMessageType, initiatorWorkflowServiceName, targetWorkflowServiceName, workflowContractName, eventQueueName); + _eventQueueConfig = new QueueConfig(eventMessageType, initiatorEventServiceName, targetEventServiceName, eventContractName, workflowQueueName); + } + + public QueueConfig GetByQueue(QueueType queue) + { + switch (queue) + { + case QueueType.Workflow: + return _workflowQueueConfig; + case QueueType.Event: + return _eventQueueConfig; + default: + throw new ArgumentOutOfRangeException(nameof(queue), queue, null); + } + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs index bde225a7c..efa82cff8 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs @@ -1,8 +1,12 @@ #region using using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; using System.Data.SqlClient; using System.Linq; +using WorkflowCore.QueueProviders.SqlServer.Interfaces; #endregion @@ -10,14 +14,35 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { public class SqlCommandExecutor : ISqlCommandExecutor { - /// - /// - /// - /// - /// - /// - /// - public SqlCommand CreateCommand(SqlConnection cn, SqlTransaction tx, string cmdtext) + public TResult ExecuteScalar(IDbConnection cn, IDbTransaction tx, string cmdtext, params DbParameter[] parameters) + { + using (var cmd = cn.CreateCommand()) + { + cmd.Transaction = tx; + cmd.CommandText = cmdtext; + + foreach (var param in parameters) + cmd.Parameters.Add(param); + + return (TResult)cmd.ExecuteScalar(); + } + } + + public int ExecuteCommand(IDbConnection cn, IDbTransaction tx, string cmdtext, params DbParameter[] parameters) + { + using (var cmd = cn.CreateCommand()) + { + cmd.Transaction = tx; + cmd.CommandText = cmdtext; + + foreach (var param in parameters) + cmd.Parameters.Add(param); + + return cmd.ExecuteNonQuery(); + } + } + + private IDbCommand CreateCommand(IDbConnection cn, IDbTransaction tx, string cmdtext) { var cmd = cn.CreateCommand(); cmd.Transaction = tx; diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index e1f2f64da..dbb43ba51 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using WorkflowCore.Interface; +using WorkflowCore.QueueProviders.SqlServer.Interfaces; #endregion @@ -16,21 +17,21 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { public class SqlServerQueueProvider : IQueueProvider { - readonly string _connectionString; + private readonly string _connectionString; - readonly bool _canMigrateDb; - readonly bool _canCreateDb; + private readonly bool _canMigrateDb; + private readonly bool _canCreateDb; - private readonly IBrokerNamesProvider _names; + private readonly IQueueConfigProvider _config; private readonly ISqlServerQueueProviderMigrator _migrator; private readonly ISqlCommandExecutor _sqlCommandExecutor; - private readonly string _queueWork; - private readonly string _dequeueWork; + private readonly string _queueWorkCommand; + private readonly string _dequeueWorkCommand; - public SqlServerQueueProvider(SqlServerQueueProviderOption opt, IBrokerNamesProvider names, ISqlServerQueueProviderMigrator migrator, ISqlCommandExecutor sqlCommandExecutor) + public SqlServerQueueProvider(SqlServerQueueProviderOptions opt, IQueueConfigProvider names, ISqlServerQueueProviderMigrator migrator, ISqlCommandExecutor sqlCommandExecutor) { - _names = names; + _config = names; _migrator = migrator; _sqlCommandExecutor = sqlCommandExecutor; _connectionString = opt.ConnectionString; @@ -39,13 +40,13 @@ public SqlServerQueueProvider(SqlServerQueueProviderOption opt, IBrokerNamesProv IsDequeueBlocking = true; - _queueWork = GetFromResource("QueueWork"); - _dequeueWork = GetFromResource("DequeueWork"); + _queueWorkCommand = GetFromResource("QueueWork"); + _dequeueWorkCommand = GetFromResource("DequeueWork"); } private static string GetFromResource(string file) { - var resName = $"WorkflowCore.QueueProviders.SqlServer.Services.{file}.sql"; + var resName = $"WorkflowCore.QueueProviders.SqlServer.SqlCommands.{file}.sql"; using (var reader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(resName))) { @@ -85,27 +86,26 @@ public void Dispose() /// public async Task QueueWork(string id, QueueType queue) { - if (String.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id), "Param id must not be null"); + if (string.IsNullOrEmpty(id)) + throw new ArgumentNullException(nameof(id), "Param id must not be null"); - SqlConnection cn = null; + SqlConnection cn = new SqlConnection(_connectionString); try { - var par = _names.GetByQueue(queue); - - cn = new SqlConnection(_connectionString); cn.Open(); - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, null, _queueWork)) - { - cmd.Parameters.AddWithValue("@initiatorService", par.InitiatorService); - cmd.Parameters.AddWithValue("@targetService", par.TargetService); - cmd.Parameters.AddWithValue("@contractName", par.ContractName); - cmd.Parameters.AddWithValue("@msgType", par.MsgType); - cmd.Parameters.AddWithValue("@RequestMessage", id); - await cmd.ExecuteNonQueryAsync(); - } - } finally + var par = _config.GetByQueue(queue); + + _sqlCommandExecutor.ExecuteCommand(cn, null, _queueWorkCommand, + new SqlParameter("@initiatorService", par.InitiatorService), + new SqlParameter("@targetService", par.TargetService), + new SqlParameter("@contractName", par.ContractName), + new SqlParameter("@msgType", par.MsgType), + new SqlParameter("@RequestMessage", id) + ); + } + finally { - cn?.Close(); + cn.Close(); } } @@ -118,23 +118,19 @@ public async Task QueueWork(string id, QueueType queue) /// Next id from queue, null if no message arrives in one second. public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) { - SqlConnection cn = null; + SqlConnection cn = new SqlConnection(_connectionString); try { - var par = _names.GetByQueue(queue); - - var sql = _dequeueWork.Replace("{queueName}", par.QueueName); - - cn = new SqlConnection(_connectionString); cn.Open(); - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, null, sql)) - { - var msg = await cmd.ExecuteScalarAsync(cancellationToken); - return msg is DBNull ? null : (string)msg; - } - } finally + var par = _config.GetByQueue(queue); + var sql = _dequeueWorkCommand.Replace("{queueName}", par.QueueName); + var msg = _sqlCommandExecutor.ExecuteScalar(cn, null, sql); + return msg is DBNull ? null : (string)msg; + + } + finally { - cn?.Close(); + cn.Close(); } } } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index ac6a17c63..7853d7800 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -6,29 +6,24 @@ using System.Text.RegularExpressions; using WorkflowCore.Interface; +using WorkflowCore.QueueProviders.SqlServer.Interfaces; #endregion namespace WorkflowCore.QueueProviders.SqlServer.Services -{ - public interface ISqlServerQueueProviderMigrator - { - void MigrateDb(); - void CreateDb(); - } +{ public class SqlServerQueueProviderMigrator : ISqlServerQueueProviderMigrator { private readonly string _connectionString; - private readonly IBrokerNamesProvider _names; + private readonly IQueueConfigProvider _configProvider; private readonly ISqlCommandExecutor _sqlCommandExecutor; - public SqlServerQueueProviderMigrator(string connectionString, IBrokerNamesProvider names, ISqlCommandExecutor sqlCommandExecutor) + public SqlServerQueueProviderMigrator(string connectionString, IQueueConfigProvider configProvider, ISqlCommandExecutor sqlCommandExecutor) { _connectionString = connectionString; - - _names = names; + _configProvider = configProvider; _sqlCommandExecutor = sqlCommandExecutor; } @@ -38,18 +33,17 @@ public SqlServerQueueProviderMigrator(string connectionString, IBrokerNamesProvi public void MigrateDb() { var cn = new SqlConnection(_connectionString); + cn.Open(); + var tx = cn.BeginTransaction(); try { - cn.Open(); - var tx = cn.BeginTransaction(); - - var n = new[] + var queueConfigurations = new[] { - _names.GetByQueue(QueueType.Workflow), - _names.GetByQueue(QueueType.Event) + _configProvider.GetByQueue(QueueType.Workflow), + _configProvider.GetByQueue(QueueType.Event) }; - foreach (var item in n) + foreach (var item in queueConfigurations) { CreateMessageType(cn, tx, item.MsgType); @@ -62,7 +56,13 @@ public void MigrateDb() } tx.Commit(); - } finally + } + catch + { + tx.Rollback(); + throw; + } + finally { cn.Close(); } @@ -71,162 +71,108 @@ public void MigrateDb() private void CreateService(SqlConnection cn, SqlTransaction tx, string name, string queueName, string contractName) { var cmdtext = @"select name from sys.services where name=@name"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) - { - cmd.Parameters.AddWithValue("@name", name); + var existing = _sqlCommandExecutor.ExecuteScalar(cn, tx, cmdtext, new SqlParameter("@name", name)); - var n = (string)cmd.ExecuteScalar(); - - if (!String.IsNullOrEmpty(n)) return; - } - - cmdtext = $"CREATE SERVICE [{name}] ON QUEUE [{queueName}]([{contractName}]);"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) - { - cmd.ExecuteNonQuery(); - } + if (!string.IsNullOrEmpty(existing)) + return; + + _sqlCommandExecutor.ExecuteCommand(cn, tx, $"CREATE SERVICE [{name}] ON QUEUE [{queueName}]([{contractName}]);"); } private void CreateQueue(SqlConnection cn, SqlTransaction tx, string queueName) { var cmdtext = @"select name from sys.service_queues where name=@name"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) - { - cmd.Parameters.AddWithValue("@name", queueName); - - var n = (string)cmd.ExecuteScalar(); + var existing = _sqlCommandExecutor.ExecuteScalar(cn, tx, cmdtext, new SqlParameter("@name", queueName)); - if (!String.IsNullOrEmpty(n)) return; - } - - cmdtext = $"CREATE QUEUE [{queueName}];"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) - { - cmd.ExecuteNonQuery(); - } + if (!string.IsNullOrEmpty(existing)) + return; + + _sqlCommandExecutor.ExecuteCommand(cn, tx, $"CREATE QUEUE [{queueName}];"); } private void CreateContract(SqlConnection cn, SqlTransaction tx, string contractName, string messageName) { var cmdtext = @"select name from sys.service_contracts where name=@name"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) - { - cmd.Parameters.AddWithValue("@name", contractName); - - var n = (string)cmd.ExecuteScalar(); - - if (!String.IsNullOrEmpty(n)) return; - } + var existing = _sqlCommandExecutor.ExecuteScalar(cn, tx, cmdtext, new SqlParameter("@name", contractName)); - cmdtext = $"CREATE CONTRACT [{contractName}] ( [{messageName}] SENT BY INITIATOR);"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) - { - cmd.ExecuteNonQuery(); - } + if (!string.IsNullOrEmpty(existing)) + return; + + _sqlCommandExecutor.ExecuteCommand(cn, tx, $"CREATE CONTRACT [{contractName}] ( [{messageName}] SENT BY INITIATOR);"); } private void CreateMessageType(SqlConnection cn, SqlTransaction tx, string message) { var cmdtext = @"select name from sys.service_message_types where name=@name"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) - { - cmd.Parameters.AddWithValue("@name", message); - - var n = (string)cmd.ExecuteScalar(); - - if (!String.IsNullOrEmpty(n)) return; - } + var existing = _sqlCommandExecutor.ExecuteScalar(cn, tx, cmdtext, new SqlParameter("@name", message)); - cmdtext = $"CREATE MESSAGE TYPE [{message}] VALIDATION = NONE;"; - using (var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext)) - { - cmd.ExecuteNonQuery(); - } + if (!string.IsNullOrEmpty(existing)) + return; + + _sqlCommandExecutor.ExecuteCommand(cn, tx, $"CREATE MESSAGE TYPE [{message}] VALIDATION = NONE;"); } #endregion public void CreateDb() { - var pattern = "Database=(.[^;]+);"; + var builder = new SqlConnectionStringBuilder(_connectionString); + var masterBuilder = new SqlConnectionStringBuilder(_connectionString); + masterBuilder.InitialCatalog = "master"; - var regex = new Regex(pattern); - var db = regex.Match(_connectionString).Groups[1].Value; - - var masterCn = _connectionString.Replace(regex.Match(_connectionString).Groups[0].Value, "Database=master;"); + var masterCnStr = masterBuilder.ToString(); bool dbPresente; - var cn = new SqlConnection(masterCn); + var cn = new SqlConnection(masterCnStr); + cn.Open(); try { - cn.Open(); - var cmd = cn.CreateCommand(); cmd.CommandText = "select name from sys.databases where name = @dbname"; - cmd.Parameters.AddWithValue("@dbname", db); + cmd.Parameters.AddWithValue("@dbname", builder.InitialCatalog); var found = cmd.ExecuteScalar(); dbPresente = (found != null); - } finally - { - cn.Close(); - } - - if (!dbPresente) - { - cn = new SqlConnection(masterCn); - try - { - cn.Open(); - var cmd = cn.CreateCommand(); - cmd.CommandText = "create database [" + db + "]"; - cmd.ExecuteNonQuery(); - } finally - { - cn.Close(); + if (!dbPresente) + { + var createCmd = cn.CreateCommand(); + createCmd.CommandText = "create database [" + builder.InitialCatalog + "]"; + createCmd.ExecuteNonQuery(); } } + finally + { + cn.Close(); + } - EnableBroker(masterCn, db); + EnableBroker(masterCnStr, builder.InitialCatalog); } private void EnableBroker(string masterCn, string db) { var cn = new SqlConnection(masterCn); - try - { - cn.Open(); - var tx = cn.BeginTransaction(); - - var cmdtext = @"select is_broker_enabled from sys.databases where name = @name"; - - var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext); - cmd.Parameters.AddWithValue("@name", db); + cn.Open(); - bool isBrokerEnabled = (bool)cmd.ExecuteScalar(); - if (isBrokerEnabled) return; + var isBrokerEnabled = _sqlCommandExecutor.ExecuteScalar(cn, null, @"select is_broker_enabled from sys.databases where name = @name", new SqlParameter("@name", db)); - tx.Commit(); - } finally - { - cn.Close(); - } + if (isBrokerEnabled) + return; - cn = new SqlConnection(masterCn); + var tx = cn.BeginTransaction(); try { - cn.Open(); - var tx = cn.BeginTransaction(); - - var cmdtext = $"ALTER DATABASE [{db}] SET ENABLE_BROKER;"; - var cmd = _sqlCommandExecutor.CreateCommand(cn, tx, cmdtext); - - cmd.ExecuteScalar(); + _sqlCommandExecutor.ExecuteCommand(cn, tx, $"ALTER DATABASE [{db}] SET ENABLE_BROKER;"); tx.Commit(); - } finally + } + catch { - cn.Close(); + tx.Rollback(); + throw; } + finally + { + cn.Close(); + } } } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/DequeueWork.sql b/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlCommands/DequeueWork.sql similarity index 100% rename from src/providers/WorkflowCore.QueueProviders.SqlServer/Services/DequeueWork.sql rename to src/providers/WorkflowCore.QueueProviders.SqlServer/SqlCommands/DequeueWork.sql diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueWork.sql b/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlCommands/QueueWork.sql similarity index 100% rename from src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueWork.sql rename to src/providers/WorkflowCore.QueueProviders.SqlServer/SqlCommands/QueueWork.sql diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOption.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOption.cs deleted file mode 100644 index 64da0aa12..000000000 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOption.cs +++ /dev/null @@ -1,22 +0,0 @@ -#region using - -using System; -using System.Linq; - -#endregion - -namespace WorkflowCore.QueueProviders.SqlServer -{ - public class SqlServerQueueProviderOption - { - public SqlServerQueueProviderOption() - { - WorkflowHostName = "default"; - } - - public string ConnectionString { get; set; } - public string WorkflowHostName { get; set; } - public bool CanMigrateDb { get; set; } - public bool CanCreateDb { get; set; } - } -} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOptions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOptions.cs new file mode 100644 index 000000000..1717739a8 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/SqlServerQueueProviderOptions.cs @@ -0,0 +1,16 @@ +#region using + +using System; +using System.Linq; + +#endregion + +namespace WorkflowCore.QueueProviders.SqlServer +{ + public class SqlServerQueueProviderOptions + { + public string ConnectionString { get; set; } + public bool CanMigrateDb { get; set; } + public bool CanCreateDb { get; set; } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 3e49c6812..87a6bef09 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -2,6 +2,10 @@ netstandard2.0 + Roberto Paterlini + Queue provider for Workflow-core using SQL Server Service Broker + + 1.0.0-alpha @@ -11,11 +15,12 @@ - - + + + diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs deleted file mode 100644 index 468a79d63..000000000 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerQueueProviderFixture.cs +++ /dev/null @@ -1,46 +0,0 @@ -#region using - -using System; -using System.Linq; - -using WorkflowCore.QueueProviders.SqlServer; -using WorkflowCore.QueueProviders.SqlServer.Services; -using WorkflowCore.UnitTests; - -using Xunit; -using Xunit.Abstractions; - -#endregion - -namespace WorkflowCore.Tests.SqlServer -{ - [Collection("SqlServer collection")] - public class SqlServerQueueProviderFixture : BaseQueueProviderFixture, IDisposable - { - public SqlServerQueueProviderFixture(ITestOutputHelper output, SqlDockerSetup setup) - { - Console = output; - var connectionString = SqlDockerSetup.ConnectionString; - - var opt = new SqlServerQueueProviderOption { - ConnectionString = connectionString, - WorkflowHostName = "UnitTest", - CanCreateDb = true, - CanMigrateDb = true - }; - var names = new BrokerNamesProvider(opt.WorkflowHostName); - var sqlCommandExecutor = new SqlCommandExecutor(); - var migrator = new SqlServerQueueProviderMigrator(opt.ConnectionString, names, sqlCommandExecutor); - - QueueProvider = new SqlServerQueueProvider(opt,names,migrator,sqlCommandExecutor); - QueueProvider.Start().Wait(); - - Setup(); - } - - public void Dispose() - { - QueueProvider.Dispose(); - } - } -} \ No newline at end of file diff --git a/test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs b/test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs deleted file mode 100644 index 7d433ee2c..000000000 --- a/test/WorkflowCore.UnitTests/BaseQueueProviderFixture.cs +++ /dev/null @@ -1,154 +0,0 @@ -#region using - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -using FluentAssertions; - -using WorkflowCore.Interface; - -using Xunit; -using Xunit.Abstractions; - -#endregion - -namespace WorkflowCore.UnitTests -{ - public abstract class BaseQueueProviderFixture - { - protected IQueueProvider QueueProvider; - protected ITestOutputHelper Console; - private bool _stop; - - #region Setup - - protected void Setup() - { - while (QueueProvider.DequeueWork(QueueType.Event, CancellationToken.None).Result != null) - { - // Empty queue before test - } - - while (QueueProvider.DequeueWork(QueueType.Workflow, CancellationToken.None).Result != null) - { - // Empty queue before test - } - } - - #endregion - - #region ShouldEnqueueAndDequeueAMessage - - [Fact] - public void ShouldEnqueueAndDequeueAMessage() - { - var id = Guid.NewGuid().ToString(); - - DoTest(id, QueueType.Event); - - id = Guid.NewGuid().ToString(); - DoTest(id, QueueType.Workflow); - } - - private void DoTest(string id, QueueType queueType) - { - QueueProvider.QueueWork(id, queueType).Wait(); - var res = QueueProvider.DequeueWork(queueType, CancellationToken.None).Result; - - res.Should().Be(id); - } - - #endregion - - #region ShouldEnqueueAndDequeueManyMessageOnManyThread - - [Fact] - public void ShouldEnqueueAndDequeueManyMessageOnManyThread() - { - const int countEvent = 250; - const int countThread = 10; - const QueueType queueType = QueueType.Event; - - var guids = new ConcurrentDictionary(); - - _stop = false; - var sw = Stopwatch.StartNew(); - - var thDeque = StartDequeueTask(countThread, queueType, guids); - var thEnque = StartEnqueueTask(countThread, countEvent, guids, queueType); - - Task.WaitAll(thEnque.ToArray()); - Console.WriteLine("Enqueue complete " + sw.ElapsedMilliseconds + " msec"); - - _stop = true; - Task.WaitAll(thDeque.ToArray()); - Console.WriteLine("Dequeue complete " + sw.ElapsedMilliseconds + " msec"); - - foreach (var guid in guids) - { - guid.Value.Should().Be(1); - } - - Console.WriteLine("Complete " + (guids.Count / (sw.ElapsedMilliseconds / 1000.0)) + " msg/sec"); - } - - private List StartEnqueueTask(int countThread, int countEvent, ConcurrentDictionary guids, QueueType queueType) - { - Console.WriteLine("Start enqueue task"); - - var thEnque = new List(); - for (int i = 0; i < countThread; i++) - { - Task t = Task.Factory.StartNew(() => - { - Console.WriteLine("-> Enqueue task " + Task.CurrentId); - - for (int j = 0; j < countEvent; j++) - { - var guid = Guid.NewGuid().ToString(); - guids.TryAdd(guid, 0); - QueueProvider.QueueWork(guid, queueType).Wait(); - } - - Console.WriteLine("<- Enqueue task " + Task.CurrentId); - }); - thEnque.Add(t); - } - - return thEnque; - } - - private List StartDequeueTask(int countThread, QueueType queueType, ConcurrentDictionary guids) - { - Console.WriteLine("Start dequeue task"); - - var thDeque = new List(); - for (int i = 0; i < countThread; i++) - { - Task t = Task.Factory.StartNew(() => - { - Console.WriteLine("-> Dequeue task " + Task.CurrentId); - while (!_stop) - { - var id = QueueProvider.DequeueWork(queueType, CancellationToken.None).Result; - if (id != null) guids.AddOrUpdate(id, 0, (key, oldval) => oldval + 1); - } - - Console.WriteLine("<- Dequeue task " + Task.CurrentId); - }); - thDeque.Add(t); - } - - return thDeque; - } - - #endregion - - - } -} \ No newline at end of file diff --git a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs index d381be03a..c20565c2c 100644 --- a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs @@ -28,7 +28,7 @@ public ExecutionResultProcessorFixture() PointerFactory = A.Fake(); DateTimeProvider = A.Fake(); - Options = new WorkflowOptions(); + Options = new WorkflowOptions(A.Fake()); A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index dff65ac25..1626eb814 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -35,7 +35,7 @@ public WorkflowExecutorFixture() ResultProcesser = A.Fake(); DateTimeProvider = A.Fake(); - Options = new WorkflowOptions(); + Options = new WorkflowOptions(A.Fake()); A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); From 1fd6507e6361610f479cc2c87904176d2b4b908b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 21 Apr 2018 18:36:31 -0700 Subject: [PATCH 029/462] fix sssb project --- .../Models/QueueConfig.cs | 12 ++++----- .../README.md | 6 +++-- .../ServiceCollectionExtensions.cs | 2 +- .../Services/QueueConfigProvider.cs | 27 +++---------------- 4 files changed, 15 insertions(+), 32 deletions(-) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs index 1bef78367..1915f487c 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs @@ -6,13 +6,13 @@ namespace WorkflowCore.QueueProviders.SqlServer.Models { public class QueueConfig { - public QueueConfig(string msgType, string initiatorService, string targetService, string contractName, string queueName) + public QueueConfig(string name) { - MsgType = msgType; - InitiatorService = initiatorService; - TargetService = targetService; - ContractName = contractName; - QueueName = queueName; + MsgType = $"//workflow-core/{name}"; + InitiatorService = $"//workflow-core/initiator{name}Service"; + TargetService = $"//workflow-core/target{name}Service"; + ContractName = $"//workflow-core/{name}Contract"; + QueueName = $"//workflow-core/{name}Queue"; } public string MsgType { get; } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md index 47055c822..2cdd0ba14 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/README.md @@ -1,5 +1,7 @@ # SQL Server Service Broker queue provider for Workflow Core +*Thank you to Roberto Paterlini for contributing this provider* + Provides distributed worker support on [Workflow Core](../../../README.md) using [SQL Server Service Broker](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-service-broker). This makes it possible to have a cluster of nodes processing your workflows, along with a distributed lock manager. @@ -14,10 +16,10 @@ PM> Install-Package WorkflowCore.QueueProviders.SqlServer -Pre ## Usage -Use the .UseSqlServerQueue extension method when building your service provider. +Use the .UseSqlServerBroker extension method when building your service provider. ```C# -services.AddWorkflow(x => x.UseSqlServerBroker(sp => new SqlServerQueueProvider(connectionString, workflowHostName, canMigrateDB)); +services.AddWorkflow(x => x.UseSqlServerBroker(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); ``` diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index f3076c523..7e2364408 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -24,7 +24,7 @@ public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, s { options.Services.AddTransient(); options.Services.AddTransient(); - options.Services.AddTransient(); + options.Services.AddTransient(sp => new SqlServerQueueProviderMigrator(connectionString, sp.GetService(), sp.GetService())); var sqlOptions = new SqlServerQueueProviderOptions() { diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs index 4b7547a0b..605b2e8a6 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs @@ -10,37 +10,18 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { - /// /// Build names for SSSB objects - /// - /// - /// Message type and contract are global, service name and queue different for every workflow host - /// + /// public class QueueConfigProvider : IQueueConfigProvider { private readonly QueueConfig _workflowQueueConfig; private readonly QueueConfig _eventQueueConfig; public QueueConfigProvider() - { - var workflowMessageType = "//workflow-core/workflow"; - var eventMessageType = "//workflow-core/event"; - - var eventContractName = "//workflow-core/eventContract"; - var workflowContractName = "//workflow-core/workflowContract"; - - var initiatorEventServiceName = $"//workflow-core/initiatorEventService"; - var targetEventServiceName = $"//workflow-core/targetEventService"; - - var initiatorWorkflowServiceName = $"//workflow-core/initiatorWorkflowService"; - var targetWorkflowServiceName = $"//workflow-core/targetWorkflowService"; - - var eventQueueName = $"//workflow-core/eventQueue"; - var workflowQueueName = $"//workflow-core/workflowQueue"; - - _workflowQueueConfig = new QueueConfig(workflowMessageType, initiatorWorkflowServiceName, targetWorkflowServiceName, workflowContractName, eventQueueName); - _eventQueueConfig = new QueueConfig(eventMessageType, initiatorEventServiceName, targetEventServiceName, eventContractName, workflowQueueName); + { + _workflowQueueConfig = new QueueConfig("workflow"); + _eventQueueConfig = new QueueConfig("event"); } public QueueConfig GetByQueue(QueueType queue) From d379c72bc0c0e00dfaf8bc30c0aa44a7019b2842 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 21 Apr 2018 18:45:31 -0700 Subject: [PATCH 030/462] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c7e9dd2f..259736b5f 100644 --- a/README.md +++ b/README.md @@ -162,9 +162,12 @@ There are several persistence providers available as separate Nuget packages. * [Testing](src/samples/WorkflowCore.TestSample01) -## Authors +## Contributors * **Daniel Gerlag** - *Initial work* +* **Jackie Ja** +* **Aaron Scribnor** +* **Roberto Paterlini** ## Ports From 7ca85e7c3cdf0d272063b35aa7f21981adea26c9 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 22 Apr 2018 12:25:36 -0700 Subject: [PATCH 031/462] update redlock package --- .../Models/Lock.cs | 34 --- .../README.md | 7 +- .../ServiceCollectionExtensions.cs | 5 +- .../Services/RedlockProvider.cs | 193 ++++-------------- .../WorkflowCore.LockProviders.Redlock.csproj | 9 +- src/samples/WorkflowCore.Sample04/Program.cs | 3 +- 6 files changed, 51 insertions(+), 200 deletions(-) delete mode 100644 src/providers/WorkflowCore.LockProviders.Redlock/Models/Lock.cs diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/Models/Lock.cs b/src/providers/WorkflowCore.LockProviders.Redlock/Models/Lock.cs deleted file mode 100644 index 01e460d69..000000000 --- a/src/providers/WorkflowCore.LockProviders.Redlock/Models/Lock.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Adapted from https://github.com/KidFashion/redlock-cs - */ -using StackExchange.Redis; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace WorkflowCore.LockProviders.Redlock.Models -{ - public class Lock - { - - public Lock(RedisKey resource, RedisValue val, TimeSpan validity) - { - this.resource = resource; - this.val = val; - this.validity_time = validity; - } - - private RedisKey resource; - - private RedisValue val; - - private TimeSpan validity_time; - - public RedisKey Resource { get { return resource; } } - - public RedisValue Value { get { return val; } } - - public TimeSpan Validity { get { return validity_time; } } - } -} diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/README.md b/src/providers/WorkflowCore.LockProviders.Redlock/README.md index 166706ee6..866fa762b 100644 --- a/src/providers/WorkflowCore.LockProviders.Redlock/README.md +++ b/src/providers/WorkflowCore.LockProviders.Redlock/README.md @@ -17,8 +17,5 @@ PM> Install-Package WorkflowCore.LockProviders.Redlock Use the .UseRedlock extension method when building your service provider. ```C# -redis = ConnectionMultiplexer.Connect("127.0.0.1"); -services.AddWorkflow(x => x.UseRedlock(redis)); -``` - -*Adapted from https://github.com/KidFashion/redlock-cs* +services.AddWorkflow(x => x.UseRedlock(new DnsEndPoint("host1", 6379), new DnsEndPoint("host2", 6379))); +``` \ No newline at end of file diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.LockProviders.Redlock/ServiceCollectionExtensions.cs index 50ae19886..2af4f0597 100644 --- a/src/providers/WorkflowCore.LockProviders.Redlock/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.LockProviders.Redlock/ServiceCollectionExtensions.cs @@ -5,14 +5,15 @@ using WorkflowCore.Models; using WorkflowCore.LockProviders.Redlock.Services; using StackExchange.Redis; +using System.Net; namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { - public static WorkflowOptions UseRedlock(this WorkflowOptions options, IConnectionMultiplexer connectionMultiplexer) + public static WorkflowOptions UseRedlock(this WorkflowOptions options, params DnsEndPoint[] endpoints) { - options.UseDistributedLockManager(sp => new RedlockProvider(connectionMultiplexer)); + options.UseDistributedLockManager(sp => new RedlockProvider(endpoints)); return options; } } diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/Services/RedlockProvider.cs b/src/providers/WorkflowCore.LockProviders.Redlock/Services/RedlockProvider.cs index 566ab6d9c..51273ac3b 100644 --- a/src/providers/WorkflowCore.LockProviders.Redlock/Services/RedlockProvider.cs +++ b/src/providers/WorkflowCore.LockProviders.Redlock/Services/RedlockProvider.cs @@ -1,199 +1,84 @@ -/* - * Adapted from https://github.com/KidFashion/redlock-cs - */ -using StackExchange.Redis; +using RedLockNet.SERedis; +using RedLockNet.SERedis.Configuration; +using RedLockNet; using System; using System.Collections.Generic; -using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; using WorkflowCore.Interface; -using WorkflowCore.LockProviders.Redlock.Models; namespace WorkflowCore.LockProviders.Redlock.Services { -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - public class RedlockProvider : IDistributedLockProvider + public class RedlockProvider : IDistributedLockProvider, IDisposable { - const int DefaultRetryCount = 3; - readonly TimeSpan DefaultRetryDelay = new TimeSpan(0, 0, 0, 0, 200); - const double ClockDriveFactor = 0.01; + private readonly RedLockFactory _redlockFactory; + private readonly TimeSpan _lockTimeout = TimeSpan.FromMinutes(10); + private readonly List ManagedLocks = new List(); - protected int Quorum { get { return (redisMasterDictionary.Count / 2) + 1; } } + public RedlockProvider(params DnsEndPoint[] endpoints) + { + var redlockEndpoints = new List(); - const String UnlockScript = @" - if redis.call(""get"",KEYS[1]) == ARGV[1] then - return redis.call(""del"",KEYS[1]) - else - return 0 - end"; + foreach (var ep in endpoints) + redlockEndpoints.Add(ep); + - private static List OwnLocks = new List(); + _redlockFactory = RedLockFactory.Create(redlockEndpoints); - public RedlockProvider(params IConnectionMultiplexer[] list) - { - foreach (var item in list) - this.redisMasterDictionary.Add(item.GetEndPoints().First().ToString(), item); } public async Task AcquireLock(string Id, CancellationToken cancellationToken) { - Lock lockObject = null; - if (Lock(Id, TimeSpan.FromMinutes(30), out lockObject)) - { - OwnLocks.Add(lockObject); - return true; - } - return false; - } + + var redLock = await _redlockFactory.CreateLockAsync(Id, _lockTimeout); - public async Task ReleaseLock(string Id) - { - var list = OwnLocks.Where(x => x.Resource == Id).ToList(); - foreach (var lockObject in list) + if (redLock.IsAcquired) { - Unlock(lockObject); - OwnLocks.Remove(lockObject); + lock (ManagedLocks) + { + ManagedLocks.Add(redLock); + } + return true; } - } - protected static byte[] CreateUniqueLockId() - { - return Guid.NewGuid().ToByteArray(); + return false; } - protected Dictionary redisMasterDictionary = new Dictionary(); - - //TODO: Refactor passing a ConnectionMultiplexer - protected bool LockInstance(string redisServer, string resource, byte[] val, TimeSpan ttl) - { - - bool succeeded; - try - { - var redis = this.redisMasterDictionary[redisServer]; - succeeded = redis.GetDatabase().StringSet(resource, val, ttl, When.NotExists); - } - catch (Exception) - { - succeeded = false; - } - return succeeded; - } - - //TODO: Refactor passing a ConnectionMultiplexer - protected void UnlockInstance(string redisServer, string resource, byte[] val) - { - RedisKey[] key = { resource }; - RedisValue[] values = { val }; - var redis = redisMasterDictionary[redisServer]; - redis.GetDatabase().ScriptEvaluate( - UnlockScript, - key, - values - ); - } - protected bool Lock(RedisKey resource, TimeSpan ttl, out Lock lockObject) + public Task ReleaseLock(string Id) { - var val = CreateUniqueLockId(); - Lock innerLock = null; - bool successfull = retry(DefaultRetryCount, DefaultRetryDelay, () => + lock (ManagedLocks) { - try + foreach (var redLock in ManagedLocks) { - int n = 0; - var startTime = DateTime.Now; - - // Use keys - for_each_redis_registered( - redis => - { - if (LockInstance(redis, resource, val, ttl)) n += 1; - } - ); - - /* - * Add 2 milliseconds to the drift to account for Redis expires - * precision, which is 1 millisecond, plus 1 millisecond min drift - * for small TTLs. - */ - var drift = Convert.ToInt32((ttl.TotalMilliseconds * ClockDriveFactor) + 2); - var validity_time = ttl - (DateTime.Now - startTime) - new TimeSpan(0, 0, 0, 0, drift); - - if (n >= Quorum && validity_time.TotalMilliseconds > 0) - { - innerLock = new Lock(resource, val, validity_time); - return true; - } - else + if (redLock.Resource == Id) { - for_each_redis_registered( - redis => - { - UnlockInstance(redis, resource, val); - } - ); - return false; + redLock.Dispose(); + ManagedLocks.Remove(redLock); + break; } } - catch (Exception) - { return false; } - }); - - lockObject = innerLock; - return successfull; - } - - protected void for_each_redis_registered(Action action) - { - foreach (var item in redisMasterDictionary) - { - action(item.Value); } - } - protected void for_each_redis_registered(Action action) - { - foreach (var item in redisMasterDictionary) - { - action(item.Key); - } + return Task.CompletedTask; } - protected bool retry(int retryCount, TimeSpan retryDelay, Func action) + public Task Start() { - int maxRetryDelay = (int)retryDelay.TotalMilliseconds; - Random rnd = new Random(); - int currentRetry = 0; - - while (currentRetry++ < retryCount) - { - if (action()) return true; - Thread.Sleep(rnd.Next(maxRetryDelay)); - } - return false; + return Task.CompletedTask; } - protected void Unlock(Lock lockObject) + public Task Stop() { - for_each_redis_registered(redis => - { - UnlockInstance(redis, lockObject.Resource, lockObject.Value); - }); + return Task.CompletedTask; } - public async Task Start() + public void Dispose() { - - } - - public async Task Stop() - { - + _redlockFactory?.Dispose(); } } -#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously -} +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj b/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj index e51b242ea..bd0db00e5 100644 --- a/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj +++ b/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj @@ -1,8 +1,8 @@ - + Workflow Core Redlock distributed lock manager - 1.1.0 + 1.6.0 Daniel Gerlag netstandard1.3 WorkflowCore.LockProviders.Redlock @@ -20,8 +20,8 @@ 1.3.0 Distributed lock provider for Workflow-core using Redis - 1.3.0.0 - 1.3.0.0 + 1.6.0.0 + 1.6.0.0 @@ -30,6 +30,7 @@ + diff --git a/src/samples/WorkflowCore.Sample04/Program.cs b/src/samples/WorkflowCore.Sample04/Program.cs index db44c287a..8ff01a463 100644 --- a/src/samples/WorkflowCore.Sample04/Program.cs +++ b/src/samples/WorkflowCore.Sample04/Program.cs @@ -58,7 +58,8 @@ private static IServiceProvider ConfigureServices() // x.UseSqlServerLocking(@"Server=.\SQLEXPRESS;Database=WorkflowCore;Trusted_Connection=True;"); //}); - //redis = ConnectionMultiplexer.Connect("127.0.0.1"); + //services.AddWorkflow(x => x.UseRedlock(new System.Net.DnsEndPoint("127.0.0.1", 32768))); + //services.AddWorkflow(x => //{ // x.UseMongoDB(@"mongodb://192.168.0.12:27017", "workflow"); From 32d83d65c4ebd5d70a4287c3fd8852f5b4686709 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 22 Apr 2018 19:38:16 -0700 Subject: [PATCH 032/462] bump versions --- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- .../WorkflowCore.LockProviders.Redlock.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index a2b677f4a..7c68b7f93 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -16,9 +16,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.0 - 1.6.0.0 - 1.6.0.0 + 1.6.1 + 1.6.1.0 + 1.6.1.0 diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj b/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj index bd0db00e5..902b41e51 100644 --- a/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj +++ b/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj @@ -17,7 +17,7 @@ false false false - 1.3.0 + 1.6.0 Distributed lock provider for Workflow-core using Redis 1.6.0.0 From 1ca54de25e136ac2812e6a283abcce8979d0c114 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 27 Apr 2018 10:51:00 -0700 Subject: [PATCH 033/462] performance tweak --- src/WorkflowCore/Models/ExecutionPointer.cs | 2 ++ .../Primitives/ContainerStepBody.cs | 25 +++++++++++-------- src/WorkflowCore/Primitives/While.cs | 17 +++++-------- src/WorkflowCore/Services/WorkflowExecutor.cs | 25 ++----------------- 4 files changed, 25 insertions(+), 44 deletions(-) diff --git a/src/WorkflowCore/Models/ExecutionPointer.cs b/src/WorkflowCore/Models/ExecutionPointer.cs index 0bb15fbee..013c130e6 100644 --- a/src/WorkflowCore/Models/ExecutionPointer.cs +++ b/src/WorkflowCore/Models/ExecutionPointer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace WorkflowCore.Models { @@ -44,6 +45,7 @@ public class ExecutionPointer public PointerStatus Status { get; set; } = PointerStatus.Legacy; public Stack Scope { get; set; } = new Stack(); + } public enum PointerStatus diff --git a/src/WorkflowCore/Primitives/ContainerStepBody.cs b/src/WorkflowCore/Primitives/ContainerStepBody.cs index 68da80ac9..fec393852 100644 --- a/src/WorkflowCore/Primitives/ContainerStepBody.cs +++ b/src/WorkflowCore/Primitives/ContainerStepBody.cs @@ -5,27 +5,32 @@ namespace WorkflowCore.Primitives { public abstract class ContainerStepBody : StepBody - { - protected bool IsBranchComplete(IEnumerable pointers, string rootId) + { + protected bool IsBranchComplete(ICollection pointers, string rootId) { - //TODO: move to own class var root = pointers.First(x => x.Id == rootId); if (root.EndTime == null) { - return false; + return false; } - var list = pointers.Where(x => x.PredecessorId == rootId).ToList(); + var queue = new Queue(pointers.Where(x => x.PredecessorId == rootId)); - bool result = true; - - foreach (var item in list) + while (queue.Count > 0) { - result = result && IsBranchComplete(pointers, item.Id); + var item = queue.Dequeue(); + if (item.EndTime == null) + { + return false; + } + + var children = pointers.Where(x => x.PredecessorId == item.Id).ToList(); + foreach (var child in children) + queue.Enqueue(child); } - return result; + return true; } } } diff --git a/src/WorkflowCore/Primitives/While.cs b/src/WorkflowCore/Primitives/While.cs index fc2b5c906..4565e7c48 100644 --- a/src/WorkflowCore/Primitives/While.cs +++ b/src/WorkflowCore/Primitives/While.cs @@ -22,19 +22,14 @@ public override ExecutionResult Run(IStepExecutionContext context) } if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) - { - bool complete = true; - foreach (var childId in context.ExecutionPointer.Children) - { - complete = complete && IsBranchComplete(context.Workflow.ExecutionPointers, childId); - } - - if (complete) + { + for (int i = context.ExecutionPointer.Children.Count - 1; i > -1; i--) { - return ExecutionResult.Persist(null); + if (!IsBranchComplete(context.Workflow.ExecutionPointers, context.ExecutionPointer.Children[i])) + return ExecutionResult.Persist(context.PersistenceData); } - - return ExecutionResult.Persist(context.PersistenceData); + + return ExecutionResult.Persist(null); //re-evaluate condition on next pass } throw new CorruptPersistenceDataException(); diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 53c5918b6..b8897ddd4 100644 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -220,7 +220,7 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) { foreach (var pointer in workflow.ExecutionPointers.Where(x => x.Active && (x.Children ?? new List()).Count > 0)) { - if (workflow.ExecutionPointers.Where(x => pointer.Children.Contains(x.Id)).All(x => IsBranchComplete(workflow.ExecutionPointers, x.Id))) + if (workflow.ExecutionPointers.Where(x => pointer.Children.Contains(x.Id)).All(x => x.EndTime.HasValue)) { if (!pointer.SleepUntil.HasValue) { @@ -240,27 +240,6 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); } } - - private bool IsBranchComplete(IEnumerable pointers, string rootId) - { - //TODO: move to own class - var root = pointers.First(x => x.Id == rootId); - - if (root.EndTime == null) - { - return false; - } - - var list = pointers.Where(x => x.PredecessorId == rootId).ToList(); - - bool result = true; - - foreach (var item in list) - { - result = result && IsBranchComplete(pointers, item.Id); - } - - return result; - } + } } From d0cb88d6510af65d3f419e312e0de16a2bca36bf Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 28 Apr 2018 12:01:18 -0700 Subject: [PATCH 034/462] empty Do block check --- .../Services/FluentBuilders/ParallelStepBuilder.cs | 8 ++++++-- src/WorkflowCore/WorkflowCore.csproj | 7 +++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/WorkflowCore/Services/FluentBuilders/ParallelStepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/ParallelStepBuilder.cs index b8617a0ad..81cb34690 100644 --- a/src/WorkflowCore/Services/FluentBuilders/ParallelStepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/ParallelStepBuilder.cs @@ -25,8 +25,12 @@ public ParallelStepBuilder(IWorkflowBuilder workflowBuilder, IStepBuilder public IParallelStepBuilder Do(Action> builder) { - int lastStep = WorkflowBuilder.LastStep; - builder.Invoke(WorkflowBuilder); + var lastStep = WorkflowBuilder.LastStep; + builder.Invoke(WorkflowBuilder); + + if (lastStep == WorkflowBuilder.LastStep) + throw new NotSupportedException("Empty Do block not supported"); + Step.Children.Add(lastStep + 1); //TODO: make more elegant return this; diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 7c68b7f93..878e3b4f4 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -2,7 +2,6 @@ Workflow Core - 1.4.0 Daniel Gerlag netstandard1.3 WorkflowCore @@ -16,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.1 - 1.6.1.0 - 1.6.1.0 + 1.6.2 + 1.6.2.0 + 1.6.2.0 From 3e323fe8d39b1a21138e9a4fcd69216aa961c3f1 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 5 May 2018 12:42:50 -0700 Subject: [PATCH 035/462] add logo --- src/WorkflowCore/WorkflowCore.csproj | 7 ++++--- src/logo.png | Bin 0 -> 460 bytes 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 src/logo.png diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 878e3b4f4..8ef298921 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,10 +15,11 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.2 - 1.6.2.0 - 1.6.2.0 + 1.6.3 + 1.6.3.0 + 1.6.3.0 + https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/src/logo.png b/src/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..aadfc93fa7928681c485d6ec1118ee099f3de3f5 GIT binary patch literal 460 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=3dtTpz6=aiY77hwEes65fIEakt5%+f1S>8hi63*PS4F%ZM9v*P$VGA)5QV^Z>WOH}Q{%*?v(>j&Eoj2~v z`5b#QahnUnrD+@vw}coyqRlG#EMnLF_;T)N#Q}BMAV%+__JLg!bQ`xmO^@1V-R0uo zuQ-)uSHnalF3z32O1Yg3OLPSrwx}{qFmaR17VSSWN%BIl%U3&vXeQm(kf-Ty;@gV_ zUBD(QF=TqPDCAD${;}=+@>?9Wu38Dcj$2t~HcU(M;uK=EZ4D86!Q|GZp%TCvB9h>} zfOCb6MKF^qL-GL~&Zp8!*=)PEd^9Y|(O+tuu;sw4qN~OWeH^Z?Uq0uK{V*Tl=xsTl*gNZ$ jydh2li9cgn_LA?QR{4jSS%$zUVPNod^>bP0l+XkKH5{Hj literal 0 HcmV?d00001 From 7760f1e500970788554065b5fb31434f1838c512 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 6 May 2018 22:11:45 -0700 Subject: [PATCH 036/462] Update WorkflowCore.Users.csproj --- .../WorkflowCore.Users/WorkflowCore.Users.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 910ec2704..909e6fcfe 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -2,7 +2,7 @@ Workflow Core extensions for human workflow - 1.6.0 + 1.6.2 Daniel Gerlag netstandard1.3 WorkflowCore.Users @@ -18,9 +18,9 @@ false false Provides extensions for Workflow Core to enable human workflows. - 1.6.0 - 1.6.0.0 - 1.6.0.0 + 1.6.2 + 1.6.2.0 + 1.6.2.0 From b1103827e983ef3de043782db0a07dc40d5c84c0 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 7 May 2018 10:03:13 -0700 Subject: [PATCH 037/462] fix serialization issue with MongoDB provider --- .../Services/DataObjectSerializer.cs | 44 ++++++++++++++++--- .../WorkflowCore.Persistence.MongoDB.csproj | 6 +-- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs index 46d0d9623..a01a770d9 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs @@ -16,7 +16,7 @@ public class DataObjectSerializer : SerializerBase { private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { - TypeNameHandling = TypeNameHandling.All + TypeNameHandling = TypeNameHandling.Objects, }; public override object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) @@ -32,15 +32,45 @@ public override object Deserialize(BsonDeserializationContext context, BsonDeser public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { - string str = JsonConvert.SerializeObject(value, SerializerSettings); + var str = JsonConvert.SerializeObject(value, SerializerSettings); var doc = BsonDocument.Parse(str); - var typeElem = doc.GetElement("$type"); - doc.RemoveElement(typeElem); - - if (doc.Elements.All(x => x.Name != "_t")) - doc.InsertAt(0, new BsonElement("_t", typeElem.Value)); + ConvertMetaFormat(doc); BsonSerializer.Serialize(context.Writer, doc); } + + private static void ConvertMetaFormat(BsonDocument root) + { + var stack = new Stack(); + stack.Push(root); + + while (stack.Count > 0) + { + var doc = stack.Pop(); + + if (doc.TryGetElement("$type", out var typeElem)) + { + doc.RemoveElement(typeElem); + + if (doc.Elements.All(x => x.Name != "_t")) + doc.InsertAt(0, new BsonElement("_t", typeElem.Value)); + } + + foreach (var subDoc in doc.Elements) + { + if (subDoc.Value.IsBsonDocument) + stack.Push(subDoc.Value.ToBsonDocument()); + + if (subDoc.Value.IsBsonArray) + { + foreach (var element in subDoc.Value.AsBsonArray) + { + if (element.IsBsonDocument) + stack.Push(element.ToBsonDocument()); + } + } + } + } + } } } diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 6d006caf0..dfe1f79cd 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -17,10 +17,10 @@ false false false - 1.6.0 + 1.6.2 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 1.6.0.0 - 1.6.0.0 + 1.6.2.0 + 1.6.2.0 From 85cf5d892db0c7440115635fd07dba4fe04b53cd Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 5 Jun 2018 13:45:15 -0700 Subject: [PATCH 038/462] MongoDB driver serialization issue --- .../Services/MongoPersistenceProvider.cs | 10 +--------- .../WorkflowCore.Persistence.MongoDB.csproj | 6 +++--- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index fea94b5bb..5d018d851 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -64,15 +64,7 @@ static MongoPersistenceProvider() x.MapProperty(y => y.IsProcessed); }); - //BsonClassMap.RegisterClassMap(x => - //{ - // x.MapIdProperty(y => y.Id) - // .SetIdGenerator(new StringObjectIdGenerator()); - // x.MapProperty(y => y.ErrorTime); - // x.MapProperty(y => y.ExecutionPointerId); - // x.MapProperty(y => y.Message); - // x.MapProperty(y => y.WorkflowId); - //}); + BsonClassMap.RegisterClassMap(x => x.AutoMap()); } static bool indexesCreated = false; diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index dfe1f79cd..35afa3a35 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -17,10 +17,10 @@ false false false - 1.6.2 + 1.6.3 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 1.6.2.0 - 1.6.2.0 + 1.6.3.0 + 1.6.3.0 From 0aa22b0b6f8d4f7a6f93a406019c50067fd1fb6e Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 8 Jun 2018 11:23:46 -0700 Subject: [PATCH 039/462] refactor --- .../Interfaces/IWorkflowDbContextFactory.cs | 12 + .../EntityFrameworkPersistenceProvider.cs | 253 +++++------------- .../Services/WorkflowDbContext.cs | 61 +++++ ...lowCore.Persistence.EntityFramework.csproj | 6 +- .../MigrationContextFactory.cs | 6 +- ...20170126230815_InitialDatabase.Designer.cs | 2 +- .../20170312161610_Events.Designer.cs | 2 +- ...170507214430_ControlStructures.Designer.cs | 2 +- .../20170519231452_PersistOutcome.Designer.cs | 2 +- .../20170722200412_WfReference.Designer.cs | 2 +- .../20171223020844_StepScope.Designer.cs | 2 +- ...ostgresPersistenceProviderModelSnapshot.cs | 2 +- ...sistenceProvider.cs => PostgresContext.cs} | 6 +- .../PostgresContextFactory.cs | 23 ++ .../ServiceCollectionExtensions.cs | 3 +- ...WorkflowCore.Persistence.PostgreSQL.csproj | 6 +- .../MigrationContextFactory.cs | 6 +- ...20170126230839_InitialDatabase.Designer.cs | 2 +- .../20170312161610_Events.Designer.cs | 2 +- ...170507214430_ControlStructures.Designer.cs | 2 +- .../20170519231452_PersistOutcome.Designer.cs | 2 +- .../20170722195832_WfReference.Designer.cs | 2 +- .../20171223020645_StepScope.Designer.cs | 2 +- ...lServerPersistenceProviderModelSnapshot.cs | 2 +- .../ServiceCollectionExtensions.cs | 7 +- .../SqlContextFactory.cs | 23 ++ ...istenceprovider.cs => SqlServerContext.cs} | 6 +- .../WorkflowCore.Persistence.SqlServer.csproj | 6 +- .../ServiceCollectionExtensions.cs | 3 +- ...ersistenceProvider.cs => SqliteContext.cs} | 6 +- .../SqliteContextFactory.cs | 23 ++ .../WorkflowCore.Persistence.Sqlite.csproj | 6 +- .../PostgresPersistenceProviderFixture.cs | 3 +- .../WorkflowCore.Tests.PostgreSQL.csproj | 2 +- .../SqlServerPersistenceProviderFixture.cs | 3 +- .../SqlitePersistenceProviderFixture.cs | 3 +- 36 files changed, 264 insertions(+), 237 deletions(-) create mode 100644 src/providers/WorkflowCore.Persistence.EntityFramework/Interfaces/IWorkflowDbContextFactory.cs create mode 100644 src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs rename src/providers/WorkflowCore.Persistence.PostgreSQL/{PostgresPersistenceProvider.cs => PostgresContext.cs} (90%) create mode 100644 src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs create mode 100644 src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs rename src/providers/WorkflowCore.Persistence.SqlServer/{SqlServerPersistenceprovider.cs => SqlServerContext.cs} (91%) rename src/providers/WorkflowCore.Persistence.Sqlite/{SqlitePersistenceProvider.cs => SqliteContext.cs} (89%) create mode 100644 src/providers/WorkflowCore.Persistence.Sqlite/SqliteContextFactory.cs diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Interfaces/IWorkflowDbContextFactory.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Interfaces/IWorkflowDbContextFactory.cs new file mode 100644 index 000000000..574f9c39f --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Interfaces/IWorkflowDbContextFactory.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Persistence.EntityFramework.Services; + +namespace WorkflowCore.Persistence.EntityFramework.Interfaces +{ + public interface IWorkflowDbContextFactory + { + WorkflowDbContext Build(); + } +} diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs index aa095a281..f29447c1f 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs @@ -4,130 +4,73 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Persistence.EntityFramework.Models; using WorkflowCore.Models; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WorkflowCore.Persistence.EntityFramework.Interfaces; namespace WorkflowCore.Persistence.EntityFramework.Services { - public abstract class EntityFrameworkPersistenceProvider : DbContext, IPersistenceProvider + public class EntityFrameworkPersistenceProvider : IPersistenceProvider + where TContext : WorkflowDbContext { - protected readonly bool _canCreateDB; - protected readonly bool _canMigrateDB; - private readonly AutoResetEvent _mutex = new AutoResetEvent(true); + private readonly bool _canCreateDB; + private readonly bool _canMigrateDB; + private readonly IWorkflowDbContextFactory _contextFactory; - protected EntityFrameworkPersistenceProvider(bool canCreateDB, bool canMigrateDB) + public EntityFrameworkPersistenceProvider(IWorkflowDbContextFactory contextFactory, bool canCreateDB, bool canMigrateDB) { + _contextFactory = contextFactory; _canCreateDB = canCreateDB; - _canMigrateDB = canMigrateDB; + _canMigrateDB = canMigrateDB; } - - protected abstract void ConfigureWorkflowStorage(EntityTypeBuilder builder); - protected abstract void ConfigureExecutionPointerStorage(EntityTypeBuilder builder); - protected abstract void ConfigureExecutionErrorStorage(EntityTypeBuilder builder); - protected abstract void ConfigureExetensionAttributeStorage(EntityTypeBuilder builder); - protected abstract void ConfigureSubscriptionStorage(EntityTypeBuilder builder); - protected abstract void ConfigureEventStorage(EntityTypeBuilder builder); - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - var workflows = modelBuilder.Entity(); - workflows.HasIndex(x => x.InstanceId).IsUnique(); - workflows.HasIndex(x => x.NextExecution); - - var executionPointers = modelBuilder.Entity(); - var executionErrors = modelBuilder.Entity(); - var extensionAttributes = modelBuilder.Entity(); - - var subscriptions = modelBuilder.Entity(); - subscriptions.HasIndex(x => x.SubscriptionId).IsUnique(); - subscriptions.HasIndex(x => x.EventName); - subscriptions.HasIndex(x => x.EventKey); - - var events = modelBuilder.Entity(); - events.HasIndex(x => x.EventId).IsUnique(); - events.HasIndex(x => new { x.EventName, x.EventKey }); - events.HasIndex(x => x.EventTime); - events.HasIndex(x => x.IsProcessed); - - ConfigureWorkflowStorage(workflows); - ConfigureExecutionPointerStorage(executionPointers); - ConfigureExecutionErrorStorage(executionErrors); - ConfigureExetensionAttributeStorage(extensionAttributes); - ConfigureSubscriptionStorage(subscriptions); - ConfigureEventStorage(events); - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - base.OnConfiguring(optionsBuilder); - optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); - } - public async Task CreateEventSubscription(EventSubscription subscription) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { subscription.Id = Guid.NewGuid().ToString(); var persistable = subscription.ToPersistable(); - var result = Set().Add(persistable); - await SaveChangesAsync(); - Entry(persistable).State = EntityState.Detached; + var result = db.Set().Add(persistable); + await db.SaveChangesAsync(); return subscription.Id; } - finally - { - _mutex.Set(); - } } public async Task CreateNewWorkflow(WorkflowInstance workflow) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { workflow.Id = Guid.NewGuid().ToString(); var persistable = workflow.ToPersistable(); - var result = Set().Add(persistable); - await SaveChangesAsync(); - Entry(persistable).State = EntityState.Detached; + var result = db.Set().Add(persistable); + await db.SaveChangesAsync(); return workflow.Id; } - finally - { - _mutex.Set(); - } } public async Task> GetRunnableInstances(DateTime asAt) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { var now = asAt.ToUniversalTime().Ticks; - var raw = await Set() + var raw = await db.Set() .Where(x => x.NextExecution.HasValue && (x.NextExecution <= now) && (x.Status == WorkflowStatus.Runnable)) .Select(x => x.InstanceId) .ToListAsync(); return raw.Select(s => s.ToString()).ToList(); } - finally - { - _mutex.Set(); - } } public async Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { - IQueryable query = Set() + IQueryable query = db.Set() .Include(wf => wf.ExecutionPointers) .ThenInclude(ep => ep.ExtensionAttributes) .Include(wf => wf.ExecutionPointers) @@ -153,19 +96,14 @@ public async Task> GetWorkflowInstances(WorkflowSt return result; } - finally - { - _mutex.Set(); - } } public async Task GetWorkflowInstance(string Id) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { var uid = new Guid(Id); - var raw = await Set() + var raw = await db.Set() .Include(wf => wf.ExecutionPointers) .ThenInclude(ep => ep.ExtensionAttributes) .Include(wf => wf.ExecutionPointers) @@ -176,19 +114,14 @@ public async Task GetWorkflowInstance(string Id) return raw.ToWorkflowInstance(); } - finally - { - _mutex.Set(); - } } public async Task PersistWorkflow(WorkflowInstance workflow) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { var uid = new Guid(workflow.Id); - var existingEntity = await Set() + var existingEntity = await db.Set() .Where(x => x.InstanceId == uid) .Include(wf => wf.ExecutionPointers) .ThenInclude(ep => ep.ExtensionAttributes) @@ -197,97 +130,70 @@ public async Task PersistWorkflow(WorkflowInstance workflow) .FirstAsync(); var persistable = workflow.ToPersistable(existingEntity); - await SaveChangesAsync(); - Entry(persistable).State = EntityState.Detached; - foreach (var ep in persistable.ExecutionPointers) - { - Entry(ep).State = EntityState.Detached; - - foreach (var attr in ep.ExtensionAttributes) - Entry(attr).State = EntityState.Detached; - - } - } - finally - { - _mutex.Set(); + await db.SaveChangesAsync(); } } public async Task TerminateSubscription(string eventSubscriptionId) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { var uid = new Guid(eventSubscriptionId); - var existing = await Set().FirstAsync(x => x.SubscriptionId == uid); - Set().Remove(existing); - await SaveChangesAsync(); - } - finally - { - _mutex.Set(); + var existing = await db.Set().FirstAsync(x => x.SubscriptionId == uid); + db.Set().Remove(existing); + await db.SaveChangesAsync(); } } public virtual void EnsureStoreExists() { - if (_canCreateDB && !_canMigrateDB) + using (var context = ConstructDbContext()) { - Database.EnsureCreated(); - return; - } + if (_canCreateDB && !_canMigrateDB) + { + context.Database.EnsureCreated(); + return; + } - if (_canMigrateDB) - { - Database.Migrate(); - return; + if (_canMigrateDB) + { + context.Database.Migrate(); + return; + } } } public async Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { asOf = asOf.ToUniversalTime(); - var raw = await Set() + var raw = await db.Set() .Where(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf) .ToListAsync(); return raw.Select(item => item.ToEventSubscription()).ToList(); } - finally - { - _mutex.Set(); - } } public async Task CreateEvent(Event newEvent) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { newEvent.Id = Guid.NewGuid().ToString(); var persistable = newEvent.ToPersistable(); - var result = Set().Add(persistable); - await SaveChangesAsync(); - Entry(persistable).State = EntityState.Detached; + var result = db.Set().Add(persistable); + await db.SaveChangesAsync(); return newEvent.Id; } - finally - { - _mutex.Set(); - } } public async Task GetEvent(string id) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { Guid uid = new Guid(id); - var raw = await Set() + var raw = await db.Set() .FirstAsync(x => x.EventId == uid); if (raw == null) @@ -295,20 +201,15 @@ public async Task GetEvent(string id) return raw.ToEvent(); } - finally - { - _mutex.Set(); - } } public async Task> GetRunnableEvents(DateTime asAt) { var now = asAt.ToUniversalTime(); - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { asAt = asAt.ToUniversalTime(); - var raw = await Set() + var raw = await db.Set() .Where(x => !x.IsProcessed) .Where(x => x.EventTime <= now) .Select(x => x.EventId) @@ -316,39 +217,28 @@ public async Task> GetRunnableEvents(DateTime asAt) return raw.Select(s => s.ToString()).ToList(); } - finally - { - _mutex.Set(); - } } public async Task MarkEventProcessed(string id) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { var uid = new Guid(id); - var existingEntity = await Set() + var existingEntity = await db.Set() .Where(x => x.EventId == uid) .AsTracking() .FirstAsync(); existingEntity.IsProcessed = true; - await SaveChangesAsync(); - Entry(existingEntity).State = EntityState.Detached; - } - finally - { - _mutex.Set(); + await db.SaveChangesAsync(); } } public async Task> GetEvents(string eventName, string eventKey, DateTime asOf) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { - var raw = await Set() + var raw = await db.Set() .Where(x => x.EventName == eventName && x.EventKey == eventKey) .Where(x => x.EventTime >= asOf) .Select(x => x.EventId) @@ -361,53 +251,44 @@ public async Task> GetEvents(string eventName, string eventK return result; } - finally - { - _mutex.Set(); - } } public async Task MarkEventUnprocessed(string id) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { var uid = new Guid(id); - var existingEntity = await Set() + var existingEntity = await db.Set() .Where(x => x.EventId == uid) .AsTracking() .FirstAsync(); existingEntity.IsProcessed = false; - await SaveChangesAsync(); - Entry(existingEntity).State = EntityState.Detached; - } - finally - { - _mutex.Set(); + await db.SaveChangesAsync(); } } public async Task PersistErrors(IEnumerable errors) { - _mutex.WaitOne(); - try + using (var db = ConstructDbContext()) { var executionErrors = errors as ExecutionError[] ?? errors.ToArray(); if (executionErrors.Any()) { foreach (var error in executionErrors) { - Set().Add(error.ToPersistable()); + db.Set().Add(error.ToPersistable()); } - await SaveChangesAsync(); + await db.SaveChangesAsync(); } } - finally - { - _mutex.Set(); - } } + + private WorkflowDbContext ConstructDbContext() + { + return _contextFactory.Build(); + } + } } diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs new file mode 100644 index 000000000..69f83cf96 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Persistence.EntityFramework.Models; + +namespace WorkflowCore.Persistence.EntityFramework.Services +{ + public abstract class WorkflowDbContext : DbContext + { + protected abstract void ConfigureWorkflowStorage(EntityTypeBuilder builder); + protected abstract void ConfigureExecutionPointerStorage(EntityTypeBuilder builder); + protected abstract void ConfigureExecutionErrorStorage(EntityTypeBuilder builder); + protected abstract void ConfigureExetensionAttributeStorage(EntityTypeBuilder builder); + protected abstract void ConfigureSubscriptionStorage(EntityTypeBuilder builder); + protected abstract void ConfigureEventStorage(EntityTypeBuilder builder); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + var workflows = modelBuilder.Entity(); + workflows.HasIndex(x => x.InstanceId).IsUnique(); + workflows.HasIndex(x => x.NextExecution); + + var executionPointers = modelBuilder.Entity(); + var executionErrors = modelBuilder.Entity(); + var extensionAttributes = modelBuilder.Entity(); + + var subscriptions = modelBuilder.Entity(); + subscriptions.HasIndex(x => x.SubscriptionId).IsUnique(); + subscriptions.HasIndex(x => x.EventName); + subscriptions.HasIndex(x => x.EventKey); + + var events = modelBuilder.Entity(); + events.HasIndex(x => x.EventId).IsUnique(); + events.HasIndex(x => new { x.EventName, x.EventKey }); + events.HasIndex(x => x.EventTime); + events.HasIndex(x => x.IsProcessed); + + ConfigureWorkflowStorage(workflows); + ConfigureExecutionPointerStorage(executionPointers); + ConfigureExecutionErrorStorage(executionErrors); + ConfigureExetensionAttributeStorage(extensionAttributes); + ConfigureSubscriptionStorage(subscriptions); + ConfigureEventStorage(events); + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 19f507742..b1c18826e 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -15,10 +15,10 @@ false false false - 1.6.1 + 1.7.0 Base package for Workflow-core peristence providers using entity framework - 1.6.1.0 - 1.6.1.0 + 1.7.0.0 + 1.7.0.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/MigrationContextFactory.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/MigrationContextFactory.cs index 10670d1ad..582034d42 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/MigrationContextFactory.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/MigrationContextFactory.cs @@ -3,11 +3,11 @@ namespace WorkflowCore.Persistence.PostgreSQL { - public class MigrationContextFactory : IDesignTimeDbContextFactory + public class MigrationContextFactory : IDesignTimeDbContextFactory { - public PostgresPersistenceProvider CreateDbContext(string[] args) + public PostgresContext CreateDbContext(string[] args) { - return new PostgresPersistenceProvider(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;Password=password;", true, true); + return new PostgresContext(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;Password=password;"); } } } diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.Designer.cs index 5e2d8870a..e2b7d8758 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.Designer.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Persistence.PostgreSQL.Migrations { - [DbContext(typeof(PostgresPersistenceProvider))] + [DbContext(typeof(PostgresContext))] [Migration("20170126230815_InitialDatabase")] partial class InitialDatabase { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.Designer.cs index b8f1f01ab..7c65f44a3 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.Designer.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Persistence.PostgreSQL.Migrations { - [DbContext(typeof(PostgresPersistenceProvider))] + [DbContext(typeof(PostgresContext))] [Migration("20170312161610_Events")] partial class Events { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.Designer.cs index ae3911ab0..551c7fcd4 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.Designer.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Persistence.PostgreSQL.Migrations { - [DbContext(typeof(PostgresPersistenceProvider))] + [DbContext(typeof(PostgresContext))] [Migration("20170507214430_ControlStructures")] partial class ControlStructures { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.Designer.cs index 646fa3ff2..be3f4ca41 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.Designer.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Persistence.PostgreSQL.Migrations { - [DbContext(typeof(PostgresPersistenceProvider))] + [DbContext(typeof(PostgresContext))] [Migration("20170519231452_PersistOutcome")] partial class PersistOutcome { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.Designer.cs index 945760e73..a614a6f57 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.Designer.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Persistence.PostgreSQL.Migrations { - [DbContext(typeof(PostgresPersistenceProvider))] + [DbContext(typeof(PostgresContext))] [Migration("20170722200412_WfReference")] partial class WfReference { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.Designer.cs index 8aaa6a901..5faad4ba3 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.Designer.cs @@ -11,7 +11,7 @@ namespace WorkflowCore.Persistence.PostgreSQL.Migrations { - [DbContext(typeof(PostgresPersistenceProvider))] + [DbContext(typeof(PostgresContext))] [Migration("20171223020844_StepScope")] partial class StepScope { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs index 7fc91a776..87447fd5e 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs @@ -11,7 +11,7 @@ namespace WorkflowCore.Persistence.PostgreSQL.Migrations { - [DbContext(typeof(PostgresPersistenceProvider))] + [DbContext(typeof(PostgresContext))] partial class PostgresPersistenceProviderModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs similarity index 90% rename from src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresPersistenceProvider.cs rename to src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs index ecd8fcda8..840f91eec 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs @@ -9,12 +9,12 @@ namespace WorkflowCore.Persistence.PostgreSQL { - public class PostgresPersistenceProvider : EntityFrameworkPersistenceProvider + public class PostgresContext : WorkflowDbContext { private readonly string _connectionString; - public PostgresPersistenceProvider(string connectionString, bool canCreateDB, bool canMigrateDB) - :base(canCreateDB, canMigrateDB) + public PostgresContext(string connectionString) + :base() { _connectionString = connectionString; } diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs new file mode 100644 index 000000000..de6d0e24d --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Persistence.EntityFramework.Interfaces; +using WorkflowCore.Persistence.EntityFramework.Services; + +namespace WorkflowCore.Persistence.PostgreSQL +{ + public class PostgresContextFactory : IWorkflowDbContextFactory + { + private readonly string _connectionString; + + public PostgresContextFactory(string connectionString) + { + _connectionString = connectionString; + } + + public WorkflowDbContext Build() + { + return new PostgresContext(_connectionString); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs index 203c336f5..1e5bd1de3 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using WorkflowCore.Models; +using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.PostgreSQL; namespace Microsoft.Extensions.DependencyInjection @@ -11,7 +12,7 @@ public static class ServiceCollectionExtensions { public static WorkflowOptions UsePostgreSQL(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB) { - options.UsePersistence(sp => new PostgresPersistenceProvider(connectionString, canCreateDB, canMigrateDB)); + options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new PostgresContextFactory(connectionString), canCreateDB, canMigrateDB)); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index dcd94315b..cf9cfc5bd 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 1.6.1 - 1.6.1.0 - 1.6.1.0 + 1.7.0 + 1.7.0.0 + 1.7.0.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/MigrationContextFactory.cs b/src/providers/WorkflowCore.Persistence.SqlServer/MigrationContextFactory.cs index fccda6b2a..5481b310d 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/MigrationContextFactory.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/MigrationContextFactory.cs @@ -3,11 +3,11 @@ namespace WorkflowCore.Persistence.SqlServer { - public class MigrationContextFactory : IDesignTimeDbContextFactory + public class MigrationContextFactory : IDesignTimeDbContextFactory { - public SqlServerPersistenceProvider CreateDbContext(string[] args) + public SqlServerContext CreateDbContext(string[] args) { - return new SqlServerPersistenceProvider(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true); + return new SqlServerContext(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;"); } } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170126230839_InitialDatabase.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170126230839_InitialDatabase.Designer.cs index 63aea02d1..8d4468da0 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170126230839_InitialDatabase.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170126230839_InitialDatabase.Designer.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Persistence.SqlServer.Migrations { - [DbContext(typeof(SqlServerPersistenceProvider))] + [DbContext(typeof(SqlServerContext))] [Migration("20170126230839_InitialDatabase")] partial class InitialDatabase { diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170312161610_Events.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170312161610_Events.Designer.cs index c06faa691..1ef0bc767 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170312161610_Events.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170312161610_Events.Designer.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Persistence.SqlServer.Migrations { - [DbContext(typeof(SqlServerPersistenceProvider))] + [DbContext(typeof(SqlServerContext))] [Migration("20170312161610_Events")] partial class Events { diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170507214430_ControlStructures.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170507214430_ControlStructures.Designer.cs index cd5bf357e..c3f8548b7 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170507214430_ControlStructures.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170507214430_ControlStructures.Designer.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Persistence.SqlServer.Migrations { - [DbContext(typeof(SqlServerPersistenceProvider))] + [DbContext(typeof(SqlServerContext))] [Migration("20170507214430_ControlStructures")] partial class ControlStructures { diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170519231452_PersistOutcome.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170519231452_PersistOutcome.Designer.cs index c9173c824..9e6f9981e 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170519231452_PersistOutcome.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170519231452_PersistOutcome.Designer.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Persistence.SqlServer.Migrations { - [DbContext(typeof(SqlServerPersistenceProvider))] + [DbContext(typeof(SqlServerContext))] [Migration("20170519231452_PersistOutcome")] partial class PersistOutcome { diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170722195832_WfReference.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170722195832_WfReference.Designer.cs index 380fb7a2c..6738a21dd 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170722195832_WfReference.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170722195832_WfReference.Designer.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Persistence.SqlServer.Migrations { - [DbContext(typeof(SqlServerPersistenceProvider))] + [DbContext(typeof(SqlServerContext))] [Migration("20170722195832_WfReference")] partial class WfReference { diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20171223020645_StepScope.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20171223020645_StepScope.Designer.cs index 21ede0209..3c12a0d96 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20171223020645_StepScope.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20171223020645_StepScope.Designer.cs @@ -11,7 +11,7 @@ namespace WorkflowCore.Persistence.SqlServer.Migrations { - [DbContext(typeof(SqlServerPersistenceProvider))] + [DbContext(typeof(SqlServerContext))] [Migration("20171223020645_StepScope")] partial class StepScope { diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs index 82e959442..313b4674b 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs @@ -11,7 +11,7 @@ namespace WorkflowCore.Persistence.SqlServer.Migrations { - [DbContext(typeof(SqlServerPersistenceProvider))] + [DbContext(typeof(SqlServerContext))] partial class SqlServerPersistenceProviderModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs index e90451e25..2faebdef6 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs @@ -1,8 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Models; +using WorkflowCore.Persistence.EntityFramework.Interfaces; +using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.SqlServer; namespace Microsoft.Extensions.DependencyInjection @@ -11,7 +10,7 @@ public static class ServiceCollectionExtensions { public static WorkflowOptions UseSqlServer(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB) { - options.UsePersistence(sp => new SqlServerPersistenceProvider(connectionString, canCreateDB, canMigrateDB)); + options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new SqlContextFactory(connectionString), canCreateDB, canMigrateDB)); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs new file mode 100644 index 000000000..28e30c4a3 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Persistence.EntityFramework.Interfaces; +using WorkflowCore.Persistence.EntityFramework.Services; + +namespace WorkflowCore.Persistence.SqlServer +{ + public class SqlContextFactory : IWorkflowDbContextFactory + { + private readonly string _connectionString; + + public SqlContextFactory(string connectionString) + { + _connectionString = connectionString; + } + + public WorkflowDbContext Build() + { + return new SqlServerContext(_connectionString); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerPersistenceprovider.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs similarity index 91% rename from src/providers/WorkflowCore.Persistence.SqlServer/SqlServerPersistenceprovider.cs rename to src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs index fde4f28e5..217ed9629 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerPersistenceprovider.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs @@ -9,12 +9,12 @@ namespace WorkflowCore.Persistence.SqlServer { - public class SqlServerPersistenceProvider : EntityFrameworkPersistenceProvider + public class SqlServerContext : WorkflowDbContext { private readonly string _connectionString; - public SqlServerPersistenceProvider(string connectionString, bool canCreateDB, bool canMigrateDB) - : base(canCreateDB, canMigrateDB) + public SqlServerContext(string connectionString) + : base() { if (!connectionString.Contains("MultipleActiveResultSets")) connectionString += ";MultipleActiveResultSets=True"; diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 6b6d81193..552e63fe5 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,10 +15,10 @@ false false false - 1.6.1 + 1.7.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 1.6.1.0 - 1.6.1.0 + 1.7.0.0 + 1.7.0.0 diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs index 476ab71c1..1c3908166 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using WorkflowCore.Models; +using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.Sqlite; namespace Microsoft.Extensions.DependencyInjection @@ -11,7 +12,7 @@ public static class ServiceCollectionExtensions { public static WorkflowOptions UseSqlite(this WorkflowOptions options, string connectionString, bool canCreateDB) { - options.UsePersistence(sp => new SqlitePersistenceProvider(connectionString, canCreateDB)); + options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new SqliteContextFactory(connectionString), canCreateDB, false)); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/SqlitePersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs similarity index 89% rename from src/providers/WorkflowCore.Persistence.Sqlite/SqlitePersistenceProvider.cs rename to src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs index 4ad3b2730..7a9737c4e 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/SqlitePersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs @@ -9,12 +9,12 @@ namespace WorkflowCore.Persistence.Sqlite { - public class SqlitePersistenceProvider : EntityFrameworkPersistenceProvider + public class SqliteContext : WorkflowDbContext { private readonly string _connectionString; - public SqlitePersistenceProvider(string connectionString, bool canCreateDB) - : base(canCreateDB, false) + public SqliteContext(string connectionString) + : base() { _connectionString = connectionString; } diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContextFactory.cs b/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContextFactory.cs new file mode 100644 index 000000000..94c9e4df8 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContextFactory.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Persistence.EntityFramework.Interfaces; +using WorkflowCore.Persistence.EntityFramework.Services; + +namespace WorkflowCore.Persistence.Sqlite +{ + public class SqliteContextFactory : IWorkflowDbContextFactory + { + private readonly string _connectionString; + + public SqliteContextFactory(string connectionString) + { + _connectionString = connectionString; + } + + public WorkflowDbContext Build() + { + return new SqliteContext(_connectionString); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 95f6bdbd5..22e30b0a9 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 1.6.1 - 1.6.1.0 - 1.6.1.0 + 1.7.0 + 1.7.0.0 + 1.7.0.0 diff --git a/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs index 94f0f30b7..09e1eb0c4 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text; using WorkflowCore.Interface; +using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.PostgreSQL; using WorkflowCore.UnitTests; using Xunit; @@ -18,7 +19,7 @@ public class PostgresPersistenceProviderFixture : BasePersistenceFixture public PostgresPersistenceProviderFixture(PostgresDockerSetup dockerSetup, ITestOutputHelper output) { output.WriteLine($"Connecting on {PostgresDockerSetup.ConnectionString}"); - _subject = new PostgresPersistenceProvider(PostgresDockerSetup.ConnectionString, true, true); + _subject = new EntityFrameworkPersistenceProvider(new PostgresContextFactory(PostgresDockerSetup.ConnectionString), true, true); _subject.EnsureStoreExists(); } } diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 197d9374d..d848e9943 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -22,7 +22,7 @@ - + diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerPersistenceProviderFixture.cs index ca82db4ab..2037caf51 100644 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerPersistenceProviderFixture.cs @@ -1,4 +1,5 @@ using WorkflowCore.Interface; +using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.SqlServer; using WorkflowCore.UnitTests; using Xunit; @@ -19,7 +20,7 @@ protected override IPersistenceProvider Subject { get { - var db = new SqlServerPersistenceProvider(_connectionString, true, true); + var db = new EntityFrameworkPersistenceProvider(new SqlContextFactory(_connectionString), true, true); db.EnsureStoreExists(); return db; } diff --git a/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs b/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs index dd4c6ca9e..0228bedfe 100644 --- a/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text; using WorkflowCore.Interface; +using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.Sqlite; using WorkflowCore.UnitTests; using Xunit; @@ -22,7 +23,7 @@ protected override IPersistenceProvider Subject { get { - var db = new SqlitePersistenceProvider(_connectionString, true); + var db = new EntityFrameworkPersistenceProvider(new SqliteContextFactory(_connectionString), true, false); db.EnsureStoreExists(); return db; } From 99e3894d41dea8403a2820255b52875af589954f Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 9 Jun 2018 17:19:17 -0700 Subject: [PATCH 040/462] refactor --- .../Services/EntityFrameworkPersistenceProvider.cs | 3 +-- .../ServiceCollectionExtensions.cs | 2 +- .../ServiceCollectionExtensions.cs | 2 +- .../ServiceCollectionExtensions.cs | 2 +- .../PostgresPersistenceProviderFixture.cs | 2 +- .../SqlServerPersistenceProviderFixture.cs | 2 +- .../SqlitePersistenceProviderFixture.cs | 2 +- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs index f29447c1f..f04f35a12 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs @@ -14,8 +14,7 @@ namespace WorkflowCore.Persistence.EntityFramework.Services { - public class EntityFrameworkPersistenceProvider : IPersistenceProvider - where TContext : WorkflowDbContext + public class EntityFrameworkPersistenceProvider : IPersistenceProvider { private readonly bool _canCreateDB; private readonly bool _canMigrateDB; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs index 1e5bd1de3..26abb29ce 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs @@ -12,7 +12,7 @@ public static class ServiceCollectionExtensions { public static WorkflowOptions UsePostgreSQL(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB) { - options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new PostgresContextFactory(connectionString), canCreateDB, canMigrateDB)); + options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new PostgresContextFactory(connectionString), canCreateDB, canMigrateDB)); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs index 2faebdef6..7b999547a 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs @@ -10,7 +10,7 @@ public static class ServiceCollectionExtensions { public static WorkflowOptions UseSqlServer(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB) { - options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new SqlContextFactory(connectionString), canCreateDB, canMigrateDB)); + options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new SqlContextFactory(connectionString), canCreateDB, canMigrateDB)); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs index 1c3908166..66edf2a88 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs @@ -12,7 +12,7 @@ public static class ServiceCollectionExtensions { public static WorkflowOptions UseSqlite(this WorkflowOptions options, string connectionString, bool canCreateDB) { - options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new SqliteContextFactory(connectionString), canCreateDB, false)); + options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new SqliteContextFactory(connectionString), canCreateDB, false)); return options; } } diff --git a/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs index 09e1eb0c4..21215f61d 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs @@ -19,7 +19,7 @@ public class PostgresPersistenceProviderFixture : BasePersistenceFixture public PostgresPersistenceProviderFixture(PostgresDockerSetup dockerSetup, ITestOutputHelper output) { output.WriteLine($"Connecting on {PostgresDockerSetup.ConnectionString}"); - _subject = new EntityFrameworkPersistenceProvider(new PostgresContextFactory(PostgresDockerSetup.ConnectionString), true, true); + _subject = new EntityFrameworkPersistenceProvider(new PostgresContextFactory(PostgresDockerSetup.ConnectionString), true, true); _subject.EnsureStoreExists(); } } diff --git a/test/WorkflowCore.Tests.SqlServer/SqlServerPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.SqlServer/SqlServerPersistenceProviderFixture.cs index 2037caf51..cbd019bb9 100644 --- a/test/WorkflowCore.Tests.SqlServer/SqlServerPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.SqlServer/SqlServerPersistenceProviderFixture.cs @@ -20,7 +20,7 @@ protected override IPersistenceProvider Subject { get { - var db = new EntityFrameworkPersistenceProvider(new SqlContextFactory(_connectionString), true, true); + var db = new EntityFrameworkPersistenceProvider(new SqlContextFactory(_connectionString), true, true); db.EnsureStoreExists(); return db; } diff --git a/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs b/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs index 0228bedfe..5a27fd530 100644 --- a/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs @@ -23,7 +23,7 @@ protected override IPersistenceProvider Subject { get { - var db = new EntityFrameworkPersistenceProvider(new SqliteContextFactory(_connectionString), true, false); + var db = new EntityFrameworkPersistenceProvider(new SqliteContextFactory(_connectionString), true, false); db.EnsureStoreExists(); return db; } From 29db20ee76fe2ab810ebf23a5784f6354cc7b804 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 11 Jun 2018 19:51:08 -0700 Subject: [PATCH 041/462] explicit registration for mongo serialisers --- .../Services/MongoPersistenceProvider.cs | 1 + .../WorkflowCore.Persistence.MongoDB.csproj | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 5d018d851..bae6f9bcd 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -65,6 +65,7 @@ static MongoPersistenceProvider() }); BsonClassMap.RegisterClassMap(x => x.AutoMap()); + BsonClassMap.RegisterClassMap(x => x.AutoMap()); } static bool indexesCreated = false; diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 35afa3a35..ef95c540a 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -17,10 +17,10 @@ false false false - 1.6.3 + 1.6.4 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 1.6.3.0 - 1.6.3.0 + 1.6.4.0 + 1.6.4.0 From 0877986d57d4cfcf3927987c756c66bfa777aaf8 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 4 Jul 2018 21:05:21 -0700 Subject: [PATCH 042/462] improve workflow scheduling --- src/WorkflowCore/Services/WorkflowExecutor.cs | 31 ++++++++++--------- src/WorkflowCore/WorkflowCore.csproj | 6 ++-- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index b8897ddd4..e8a1cb528 100644 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -199,6 +199,7 @@ private void ProcessAfterExecutionIteration(WorkflowInstance workflow, WorkflowD private void DetermineNextExecutionTime(WorkflowInstance workflow) { + //TODO: move to own class workflow.NextExecution = null; if (workflow.Status == WorkflowStatus.Complete) @@ -212,7 +213,7 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) return; } - long pointerSleep = pointer.SleepUntil.Value.ToUniversalTime().Ticks; + var pointerSleep = pointer.SleepUntil.Value.ToUniversalTime().Ticks; workflow.NextExecution = Math.Min(pointerSleep, workflow.NextExecution ?? pointerSleep); } @@ -220,25 +221,25 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) { foreach (var pointer in workflow.ExecutionPointers.Where(x => x.Active && (x.Children ?? new List()).Count > 0)) { - if (workflow.ExecutionPointers.Where(x => pointer.Children.Contains(x.Id)).All(x => x.EndTime.HasValue)) + if (!workflow.ExecutionPointers.Where(x => x.Scope.Contains(pointer.Id)).All(x => x.EndTime.HasValue)) + continue; + + if (!pointer.SleepUntil.HasValue) { - if (!pointer.SleepUntil.HasValue) - { - workflow.NextExecution = 0; - return; - } - - long pointerSleep = pointer.SleepUntil.Value.ToUniversalTime().Ticks; - workflow.NextExecution = Math.Min(pointerSleep, workflow.NextExecution ?? pointerSleep); + workflow.NextExecution = 0; + return; } + + var pointerSleep = pointer.SleepUntil.Value.ToUniversalTime().Ticks; + workflow.NextExecution = Math.Min(pointerSleep, workflow.NextExecution ?? pointerSleep); } } - if ((workflow.NextExecution == null) && (workflow.ExecutionPointers.All(x => x.EndTime != null))) - { - workflow.Status = WorkflowStatus.Complete; - workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); - } + if ((workflow.NextExecution != null) || (workflow.ExecutionPointers.Any(x => x.EndTime == null))) + return; + + workflow.Status = WorkflowStatus.Complete; + workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 8ef298921..070a1cb65 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.3 - 1.6.3.0 - 1.6.3.0 + 1.6.4 + 1.6.4.0 + 1.6.4.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From 6e46843e1adf1c2615ca8855d37f42d92c4be52d Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 11 Jul 2018 00:04:42 +0800 Subject: [PATCH 043/462] Added optional parameter 'Reference' to IWorkflowController.StartWorkflow --- .../Interface/IWorkflowController.cs | 8 ++++---- src/WorkflowCore/Services/WorkflowController.cs | 17 +++++++++-------- src/WorkflowCore/Services/WorkflowHost.cs | 16 ++++++++-------- .../Controllers/WorkflowsController.cs | 6 +++--- src/samples/WorkflowCore.Sample01/Program.cs | 2 +- src/samples/WorkflowCore.Sample02/Program.cs | 2 +- src/samples/WorkflowCore.Sample05/Program.cs | 2 +- src/samples/WorkflowCore.Sample06/Program.cs | 2 +- src/samples/WorkflowCore.Sample15/Program.cs | 2 +- 9 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/WorkflowCore/Interface/IWorkflowController.cs b/src/WorkflowCore/Interface/IWorkflowController.cs index 48b7ca405..e0639aa96 100644 --- a/src/WorkflowCore/Interface/IWorkflowController.cs +++ b/src/WorkflowCore/Interface/IWorkflowController.cs @@ -7,10 +7,10 @@ namespace WorkflowCore.Interface { public interface IWorkflowController { - Task StartWorkflow(string workflowId, object data = null); - Task StartWorkflow(string workflowId, int? version, object data = null); - Task StartWorkflow(string workflowId, TData data = null) where TData : class; - Task StartWorkflow(string workflowId, int? version, TData data = null) where TData : class; + Task StartWorkflow(string workflowId, object data = null, string reference=null); + Task StartWorkflow(string workflowId, int? version, object data = null, string reference=null); + Task StartWorkflow(string workflowId, TData data = null, string reference=null) where TData : class; + Task StartWorkflow(string workflowId, int? version, TData data = null, string reference=null) where TData : class; Task PublishEvent(string eventName, string eventKey, object eventData, DateTime? effectiveDate = null); void RegisterWorkflow() where TWorkflow : IWorkflow, new(); diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 5a124728f..7b5967bb3 100644 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -29,23 +29,23 @@ public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLoc _logger = loggerFactory.CreateLogger(); } - public Task StartWorkflow(string workflowId, object data = null) + public Task StartWorkflow(string workflowId, object data = null, string reference=null) { - return StartWorkflow(workflowId, null, data); + return StartWorkflow(workflowId, null, data, reference); } - public Task StartWorkflow(string workflowId, int? version, object data = null) + public Task StartWorkflow(string workflowId, int? version, object data = null, string reference=null) { - return StartWorkflow(workflowId, version, data); + return StartWorkflow(workflowId, version, data, reference); } - public Task StartWorkflow(string workflowId, TData data = null) + public Task StartWorkflow(string workflowId, TData data = null, string reference=null) where TData : class { - return StartWorkflow(workflowId, null, data); + return StartWorkflow(workflowId, null, data, reference); } - public async Task StartWorkflow(string workflowId, int? version, TData data = null) + public async Task StartWorkflow(string workflowId, int? version, TData data = null, string reference=null) where TData : class { @@ -63,7 +63,8 @@ public async Task StartWorkflow(string workflowId, int? version, Description = def.Description, NextExecution = 0, CreateTime = DateTime.Now.ToUniversalTime(), - Status = WorkflowStatus.Runnable + Status = WorkflowStatus.Runnable, + Reference = reference }; if ((def.DataType != null) && (data == null)) diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index f9cc2d1e8..21cace223 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -43,27 +43,27 @@ public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueP persistenceStore.EnsureStoreExists(); } - public Task StartWorkflow(string workflowId, object data = null) + public Task StartWorkflow(string workflowId, object data = null, string reference=null) { - return _workflowController.StartWorkflow(workflowId, data); + return _workflowController.StartWorkflow(workflowId, data, reference); } - public Task StartWorkflow(string workflowId, int? version, object data = null) + public Task StartWorkflow(string workflowId, int? version, object data = null, string reference=null) { - return _workflowController.StartWorkflow(workflowId, version, data); + return _workflowController.StartWorkflow(workflowId, version, data, reference); } - public Task StartWorkflow(string workflowId, TData data = null) + public Task StartWorkflow(string workflowId, TData data = null, string reference=null) where TData : class { - return _workflowController.StartWorkflow(workflowId, null, data); + return _workflowController.StartWorkflow(workflowId, null, data, reference); } - public Task StartWorkflow(string workflowId, int? version, TData data = null) + public Task StartWorkflow(string workflowId, int? version, TData data = null, string reference=null) where TData : class { - return _workflowController.StartWorkflow(workflowId, version, data); + return _workflowController.StartWorkflow(workflowId, version, data, reference); } public Task PublishEvent(string eventName, string eventKey, object eventData, DateTime? effectiveDate = null) diff --git a/src/extensions/WorkflowCore.WebAPI/Controllers/WorkflowsController.cs b/src/extensions/WorkflowCore.WebAPI/Controllers/WorkflowsController.cs index ef4da1aa3..f4200b13c 100644 --- a/src/extensions/WorkflowCore.WebAPI/Controllers/WorkflowsController.cs +++ b/src/extensions/WorkflowCore.WebAPI/Controllers/WorkflowsController.cs @@ -45,7 +45,7 @@ public async Task Get(string id) [HttpPost("{id}")] [HttpPost("{id}/{version}")] - public async Task Post(string id, int? version, [FromBody]JObject data) + public async Task Post(string id, int? version, string reference, [FromBody]JObject data) { string workflowId = null; var def = _registry.GetDefinition(id, version); @@ -55,11 +55,11 @@ public async Task Post(string id, int? version, [FromBody]JObject { var dataStr = JsonConvert.SerializeObject(data); var dataObj = JsonConvert.DeserializeObject(dataStr, def.DataType); - workflowId = await _workflowHost.StartWorkflow(id, version, dataObj); + workflowId = await _workflowHost.StartWorkflow(id, version, dataObj, reference); } else { - workflowId = await _workflowHost.StartWorkflow(id, version, null); + workflowId = await _workflowHost.StartWorkflow(id, version, null, reference); } return Ok(workflowId); diff --git a/src/samples/WorkflowCore.Sample01/Program.cs b/src/samples/WorkflowCore.Sample01/Program.cs index 2f1e6105c..1e96bd198 100644 --- a/src/samples/WorkflowCore.Sample01/Program.cs +++ b/src/samples/WorkflowCore.Sample01/Program.cs @@ -22,7 +22,7 @@ public static void Main(string[] args) host.RegisterWorkflow(); host.Start(); - host.StartWorkflow("HelloWorld", 1, null); + host.StartWorkflow("HelloWorld", 1, null, null); Console.ReadLine(); host.Stop(); diff --git a/src/samples/WorkflowCore.Sample02/Program.cs b/src/samples/WorkflowCore.Sample02/Program.cs index 49153381f..c9b87e26a 100644 --- a/src/samples/WorkflowCore.Sample02/Program.cs +++ b/src/samples/WorkflowCore.Sample02/Program.cs @@ -21,7 +21,7 @@ public static void Main(string[] args) host.RegisterWorkflow(); host.Start(); - host.StartWorkflow("Simple Decision Workflow", 1, null); + host.StartWorkflow("Simple Decision Workflow", 1, null, null); Console.ReadLine(); host.Stop(); diff --git a/src/samples/WorkflowCore.Sample05/Program.cs b/src/samples/WorkflowCore.Sample05/Program.cs index ef9f0d265..4e9f77b07 100644 --- a/src/samples/WorkflowCore.Sample05/Program.cs +++ b/src/samples/WorkflowCore.Sample05/Program.cs @@ -22,7 +22,7 @@ public static void Main(string[] args) host.RegisterWorkflow(); host.Start(); - host.StartWorkflow("DeferSampleWorkflow", 1, null); + host.StartWorkflow("DeferSampleWorkflow", 1, null, null); Console.ReadLine(); host.Stop(); diff --git a/src/samples/WorkflowCore.Sample06/Program.cs b/src/samples/WorkflowCore.Sample06/Program.cs index 1805377fb..b0bb656a4 100644 --- a/src/samples/WorkflowCore.Sample06/Program.cs +++ b/src/samples/WorkflowCore.Sample06/Program.cs @@ -22,7 +22,7 @@ public static void Main(string[] args) host.RegisterWorkflow(); host.Start(); - host.StartWorkflow("MultipleOutcomeWorkflow", 1, null); + host.StartWorkflow("MultipleOutcomeWorkflow", 1, null, null); Console.ReadLine(); host.Stop(); diff --git a/src/samples/WorkflowCore.Sample15/Program.cs b/src/samples/WorkflowCore.Sample15/Program.cs index 64675b0de..04d4c2a58 100644 --- a/src/samples/WorkflowCore.Sample15/Program.cs +++ b/src/samples/WorkflowCore.Sample15/Program.cs @@ -18,7 +18,7 @@ public static void Main(string[] args) host.RegisterWorkflow(); host.Start(); - host.StartWorkflow("HelloWorld", 1, null); + host.StartWorkflow("HelloWorld", 1, null, null); Console.ReadLine(); host.Stop(); From de7b37eca15095c9a04f4084c230b46f8736d015 Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 11 Jul 2018 00:20:48 +0800 Subject: [PATCH 044/462] Added validation to persistance of WorkflowInstance property Reference --- test/WorkflowCore.UnitTests/BasePersistenceFixture.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs index 0b100ce55..97cdd0fa1 100644 --- a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs +++ b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs @@ -49,7 +49,8 @@ public void GetWorkflowInstance_should_retrieve_workflow() Status = WorkflowStatus.Runnable, NextExecution = 0, Version = 1, - WorkflowDefinitionId = "My Workflow" + WorkflowDefinitionId = "My Workflow", + Reference = "My Reference" }; workflow.ExecutionPointers.Add(new ExecutionPointer() { @@ -76,7 +77,8 @@ public void PersistWorkflow() NextExecution = 0, Version = 1, WorkflowDefinitionId = "My Workflow", - CreateTime = new DateTime(2000, 1, 1).ToUniversalTime() + CreateTime = new DateTime(2000, 1, 1).ToUniversalTime(), + Reference = "My Reference" }; oldWorkflow.ExecutionPointers.Add(new ExecutionPointer() { @@ -87,6 +89,7 @@ public void PersistWorkflow() var workflowId = Subject.CreateNewWorkflow(oldWorkflow).Result; var newWorkflow = Utils.DeepCopy(oldWorkflow); newWorkflow.Data = oldWorkflow.Data; + newWorkflow.Reference = oldWorkflow.Reference; newWorkflow.NextExecution = 7; newWorkflow.ExecutionPointers.Add(new ExecutionPointer() { Id = Guid.NewGuid().ToString(), Active = true, StepId = 1 }); From 9a7ae011b6467923ff32a75ba050daa42aacddd7 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 10 Jul 2018 20:17:50 -0700 Subject: [PATCH 045/462] issue #155 --- src/WorkflowCore/Services/WorkflowExecutor.cs | 4 +++- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index e8a1cb528..183144065 100644 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -182,7 +182,9 @@ private void ProcessOutputs(WorkflowInstance workflow, WorkflowStep step, IStepB var member = (output.Target.Body as MemberExpression); var resolvedValue = output.Source.Compile().DynamicInvoke(body); var data = workflow.Data; - data.GetType().GetProperty(member.Member.Name).SetValue(data, resolvedValue); + var property = data.GetType().GetProperty(member.Member.Name); + var convertedValue = Convert.ChangeType(resolvedValue, property.PropertyType); + property.SetValue(data, convertedValue); } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 070a1cb65..b79394c80 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.4 - 1.6.4.0 - 1.6.4.0 + 1.6.5 + 1.6.5.0 + 1.6.5.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From 5bc845b7dc44bc58bfced70939577b337b5a0ac8 Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 18 Jul 2018 22:09:04 +0800 Subject: [PATCH 046/462] Updated version to 1.6.5 and simplified call to StartwWrkflow in samples 01 and 02 --- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- src/samples/WorkflowCore.Sample01/Program.cs | 2 +- src/samples/WorkflowCore.Sample02/Program.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 070a1cb65..b79394c80 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.4 - 1.6.4.0 - 1.6.4.0 + 1.6.5 + 1.6.5.0 + 1.6.5.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/src/samples/WorkflowCore.Sample01/Program.cs b/src/samples/WorkflowCore.Sample01/Program.cs index 1e96bd198..4a4c82bc7 100644 --- a/src/samples/WorkflowCore.Sample01/Program.cs +++ b/src/samples/WorkflowCore.Sample01/Program.cs @@ -22,7 +22,7 @@ public static void Main(string[] args) host.RegisterWorkflow(); host.Start(); - host.StartWorkflow("HelloWorld", 1, null, null); + host.StartWorkflow("HelloWorld"); Console.ReadLine(); host.Stop(); diff --git a/src/samples/WorkflowCore.Sample02/Program.cs b/src/samples/WorkflowCore.Sample02/Program.cs index c9b87e26a..77b395a26 100644 --- a/src/samples/WorkflowCore.Sample02/Program.cs +++ b/src/samples/WorkflowCore.Sample02/Program.cs @@ -21,7 +21,7 @@ public static void Main(string[] args) host.RegisterWorkflow(); host.Start(); - host.StartWorkflow("Simple Decision Workflow", 1, null, null); + host.StartWorkflow("Simple Decision Workflow"); Console.ReadLine(); host.Stop(); From 41403dcd7915a54218c19de910836a57bac44cb1 Mon Sep 17 00:00:00 2001 From: Arvin Kahbazi Date: Sat, 21 Jul 2018 11:01:52 +0430 Subject: [PATCH 047/462] Set IServiceCollection as return type for AddWorkFlow --- src/WorkflowCore/ServiceCollectionExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index fd464823a..fada4e25d 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -16,7 +16,7 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { - public static void AddWorkflow(this IServiceCollection services, Action setupAction = null) + public static IServiceCollection AddWorkflow(this IServiceCollection services, Action setupAction = null) { if (services.Any(x => x.ServiceType == typeof(WorkflowOptions))) throw new InvalidOperationException("Workflow services already registered"); @@ -47,6 +47,8 @@ public static void AddWorkflow(this IServiceCollection services, Action(); services.AddTransient(); + + return services; } } } From 534ff2206e98c1bb158c7292eaf7a5888b324876 Mon Sep 17 00:00:00 2001 From: Arvin Kahbazi Date: Sun, 22 Jul 2018 09:55:28 +0430 Subject: [PATCH 048/462] Remove reflection for creating Data --- .../Interface/IWorkflowController.cs | 4 ++-- .../Services/WorkflowController.cs | 16 ++++++------- src/WorkflowCore/Services/WorkflowHost.cs | 24 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/WorkflowCore/Interface/IWorkflowController.cs b/src/WorkflowCore/Interface/IWorkflowController.cs index 48b7ca405..da7fb0609 100644 --- a/src/WorkflowCore/Interface/IWorkflowController.cs +++ b/src/WorkflowCore/Interface/IWorkflowController.cs @@ -9,8 +9,8 @@ public interface IWorkflowController { Task StartWorkflow(string workflowId, object data = null); Task StartWorkflow(string workflowId, int? version, object data = null); - Task StartWorkflow(string workflowId, TData data = null) where TData : class; - Task StartWorkflow(string workflowId, int? version, TData data = null) where TData : class; + Task StartWorkflow(string workflowId, TData data = null) where TData : class, new(); + Task StartWorkflow(string workflowId, int? version, TData data = null) where TData : class, new(); Task PublishEvent(string eventName, string eventKey, object eventData, DateTime? effectiveDate = null); void RegisterWorkflow() where TWorkflow : IWorkflow, new(); diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 5a124728f..f4662756b 100644 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -18,7 +18,7 @@ public class WorkflowController : IWorkflowController private readonly IQueueProvider _queueProvider; private readonly IExecutionPointerFactory _pointerFactory; private readonly ILogger _logger; - + public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILoggerFactory loggerFactory) { _persistenceStore = persistenceStore; @@ -39,16 +39,16 @@ public Task StartWorkflow(string workflowId, int? version, object data = return StartWorkflow(workflowId, version, data); } - public Task StartWorkflow(string workflowId, TData data = null) - where TData : class + public Task StartWorkflow(string workflowId, TData data = null) + where TData : class, new() { return StartWorkflow(workflowId, null, data); } public async Task StartWorkflow(string workflowId, int? version, TData data = null) - where TData : class + where TData : class, new() { - + var def = _registry.GetDefinition(workflowId, version); if (def == null) { @@ -68,7 +68,7 @@ public async Task StartWorkflow(string workflowId, int? version, if ((def.DataType != null) && (data == null)) { - wf.Data = TypeExtensions.GetConstructor(def.DataType, new Type[] { }).Invoke(null); + wf.Data = new TData(); } wf.ExecutionPointers.Add(_pointerFactory.BuildGenesisPointer(def)); @@ -101,7 +101,7 @@ public async Task SuspendWorkflow(string workflowId) { if (!await _lockProvider.AcquireLock(workflowId, new CancellationToken())) return false; - + try { var wf = await _persistenceStore.GetWorkflowInstance(workflowId); @@ -168,7 +168,7 @@ public async Task TerminateWorkflow(string workflowId) await _lockProvider.ReleaseLock(workflowId); } } - + public void RegisterWorkflow() where TWorkflow : IWorkflow, new() { diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index f9cc2d1e8..675d7d0a9 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -12,8 +12,8 @@ namespace WorkflowCore.Services { public class WorkflowHost : IWorkflowHost, IDisposable - { - protected bool _shutdown = true; + { + protected bool _shutdown = true; protected readonly IServiceProvider _serviceProvider; private readonly IEnumerable _backgroundTasks; @@ -54,14 +54,14 @@ public Task StartWorkflow(string workflowId, int? version, object data = } public Task StartWorkflow(string workflowId, TData data = null) - where TData : class + where TData : class, new() { return _workflowController.StartWorkflow(workflowId, null, data); } public Task StartWorkflow(string workflowId, int? version, TData data = null) - where TData : class + where TData : class, new() { return _workflowController.StartWorkflow(workflowId, version, data); } @@ -72,7 +72,7 @@ public Task PublishEvent(string eventName, string eventKey, object eventData, Da } public void Start() - { + { _shutdown = false; PersistenceStore.EnsureStoreExists(); QueueProvider.Start().Wait(); @@ -86,26 +86,26 @@ public void Start() public void Stop() { - _shutdown = true; - + _shutdown = true; + Logger.LogInformation("Stopping background tasks"); foreach (var th in _backgroundTasks) th.Stop(); - + Logger.LogInformation("Worker tasks stopped"); - + QueueProvider.Stop(); LockProvider.Stop(); } - - public void RegisterWorkflow() + + public void RegisterWorkflow() where TWorkflow : IWorkflow, new() { TWorkflow wf = new TWorkflow(); Registry.RegisterWorkflow(wf); } - public void RegisterWorkflow() + public void RegisterWorkflow() where TWorkflow : IWorkflow, new() where TData : new() { From ec40d503386c763c7b393e8b521b95de3b74ea8a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 22 Jul 2018 07:10:05 -0700 Subject: [PATCH 049/462] Release notes --- WorkflowCore.sln | 3 ++- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 9a7d80b66..81f420925 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2026 +VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF47161E-E399-451C-BDE8-E92AAD3BD761}" EndProject @@ -91,6 +91,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.3.3.md = ReleaseNotes\1.3.3.md ReleaseNotes\1.4.0.md = ReleaseNotes\1.4.0.md ReleaseNotes\1.6.0.md = ReleaseNotes\1.6.0.md + ReleaseNotes\1.6.6.md = ReleaseNotes\1.6.6.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index b79394c80..4dc0b9883 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.5 - 1.6.5.0 - 1.6.5.0 + 1.6.6 + 1.6.6.0 + 1.6.6.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From 3430a77df538d5b8875dcc175c9063fd77d633e6 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 22 Jul 2018 07:28:24 -0700 Subject: [PATCH 050/462] release notes --- ReleaseNotes/1.6.6.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 ReleaseNotes/1.6.6.md diff --git a/ReleaseNotes/1.6.6.md b/ReleaseNotes/1.6.6.md new file mode 100644 index 000000000..5355bdb10 --- /dev/null +++ b/ReleaseNotes/1.6.6.md @@ -0,0 +1,3 @@ +# Workflow Core 1.6.6 + +* Added optional `Reference` parameter to StartWorkflow methods \ No newline at end of file From d154182c9058905a588a0d4b51622b119bd96044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Wed, 22 Aug 2018 21:40:30 +0200 Subject: [PATCH 051/462] Added support for indexers instead of properties for the DataType. Allowing Dictionaries to be used with a variable number of properties. --- .../DefinitionStorage/DefinitionLoader.cs | 31 ++++++++--- .../FluentBuilders/ExpressionHelpers.cs | 54 +++++++++++++++++++ src/WorkflowCore/Services/WorkflowExecutor.cs | 8 +-- .../PassingDataWorkflow2.cs | 38 +++++++++++++ src/samples/WorkflowCore.Sample03/Program.cs | 11 ++++ 5 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs create mode 100644 src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs diff --git a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs index 28b1a3643..4c7af8831 100644 --- a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs +++ b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs @@ -173,12 +173,15 @@ private void AttachInputs(StepSourceV1 source, Type dataType, Type stepType, Wor var dataParameter = Expression.Parameter(dataType, "data"); var contextParameter = Expression.Parameter(typeof(IStepExecutionContext), "context"); var sourceExpr = DynamicExpressionParser.ParseLambda(new [] { dataParameter, contextParameter }, typeof(object), input.Value); - var targetExpr = Expression.Property(Expression.Parameter(stepType), input.Key); - step.Inputs.Add(new DataMapping() + var stepParameter = Expression.Parameter(stepType, "step"); + var targetProperty = Expression.Property(stepParameter, input.Key); + var targetExpr = Expression.Lambda(targetProperty, stepParameter); + + step.Inputs.Add(new DataMapping { Source = sourceExpr, - Target = Expression.Lambda(targetExpr) + Target = targetExpr }); } } @@ -189,12 +192,28 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo { var stepParameter = Expression.Parameter(stepType, "step"); var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { stepParameter }, typeof(object), output.Value); - var targetExpr = Expression.Property(Expression.Parameter(dataType), output.Key); - step.Outputs.Add(new DataMapping() + var dataParameter = Expression.Parameter(dataType, "data"); + Expression targetProperty; + + // Check if our datatype has a matching property + var propertyInfo = dataType.GetProperty(output.Key); + if (propertyInfo != null) + { + targetProperty = Expression.Property(dataParameter, propertyInfo); + } + else + { + // If we did not find a matching property try to find a Indexer with string parameter + propertyInfo = dataType.GetProperty("Item"); + targetProperty = Expression.Property(dataParameter, propertyInfo, Expression.Constant(output.Key)); + } + var targetExpr = Expression.Lambda(targetProperty, dataParameter); + + step.Outputs.Add(new DataMapping { Source = sourceExpr, - Target = Expression.Lambda(targetExpr) + Target = targetExpr }); } } diff --git a/src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs b/src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs new file mode 100644 index 000000000..aa78714af --- /dev/null +++ b/src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs @@ -0,0 +1,54 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace WorkflowCore.Services.FluentBuilders +{ + internal static class ExpressionHelpers + { + public static Expression> CreateSetter( + Expression> getterExpression) + { + var valueParameter = Expression.Parameter(typeof(TParam), "value"); + Expression assignment; + if (getterExpression.Body is MethodCallExpression callExpression && callExpression.Method.Name == "get_Item") + { + //Get Matching setter method for the indexer + var parameterTypes = callExpression.Method.GetParameters() + .Select(p => p.ParameterType) + .ToArray(); + var itemProperty = callExpression.Method.DeclaringType.GetProperty("Item", typeof(TParam), parameterTypes); + + assignment = Expression.Call(callExpression.Object, itemProperty.SetMethod, callExpression.Arguments.Concat(new[] { valueParameter })); + } + else + { + assignment = Expression.Assign(getterExpression.Body, valueParameter); + } + return Expression.Lambda>(assignment, valueParameter); + } + + public static LambdaExpression CreateSetter(LambdaExpression getterExpression) + { + var thisParameter = getterExpression.Parameters.Single(); + var valueParameter = Expression.Parameter(getterExpression.ReturnType, "value"); + Expression assignment; + if (getterExpression.Body is MethodCallExpression callExpression && callExpression.Method.Name == "get_Item") + { + //Get Matching setter method for the indexer + var parameterTypes = callExpression.Method.GetParameters() + .Select(p => p.ParameterType) + .ToArray(); + var itemProperty = callExpression.Method.DeclaringType.GetProperty("Item", valueParameter.Type, parameterTypes); + + assignment = Expression.Call(callExpression.Object, itemProperty.SetMethod, callExpression.Arguments.Concat(new[] { valueParameter })); + } + else + { + assignment = Expression.Assign(getterExpression.Body, valueParameter); + } + return Expression.Lambda(assignment, thisParameter, valueParameter); + } + } +} diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 183144065..06f33ac12 100644 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Models; +using WorkflowCore.Services.FluentBuilders; namespace WorkflowCore.Services { @@ -179,12 +180,11 @@ private void ProcessOutputs(WorkflowInstance workflow, WorkflowStep step, IStepB { foreach (var output in step.Outputs) { - var member = (output.Target.Body as MemberExpression); var resolvedValue = output.Source.Compile().DynamicInvoke(body); var data = workflow.Data; - var property = data.GetType().GetProperty(member.Member.Name); - var convertedValue = Convert.ChangeType(resolvedValue, property.PropertyType); - property.SetValue(data, convertedValue); + var setter = ExpressionHelpers.CreateSetter(output.Target); + var convertedValue = Convert.ChangeType(resolvedValue, setter.Parameters[1].Type); + setter.Compile().DynamicInvoke(data, convertedValue); } } diff --git a/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs b/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs new file mode 100644 index 000000000..482ea1229 --- /dev/null +++ b/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Sample03.Steps; + +namespace WorkflowCore.Sample03 +{ + public class PassingDataWorkflow2 : IWorkflow> + { + public void Build(IWorkflowBuilder> builder) + { + builder + .StartWith(context => + { + Console.WriteLine("Starting workflow..."); + return ExecutionResult.Next(); + }) + .Then() + .Input(step => step.Input1, data => data["Value1"]) + .Input(step => step.Input2, data => data["Value2"]) + .Output(data => data["Value3"], step => step.Output) + .Then() + .Name("Print custom message") + .Input(step => step.Message, data => "The answer is " + data["Value3"].ToString()) + .Then(context => + { + Console.WriteLine("Workflow complete"); + return ExecutionResult.Next(); + }); + } + + public string Id => "PassingDataWorkflow2"; + + public int Version => 1; + + } +} diff --git a/src/samples/WorkflowCore.Sample03/Program.cs b/src/samples/WorkflowCore.Sample03/Program.cs index d778ba6ba..4b65d75a2 100644 --- a/src/samples/WorkflowCore.Sample03/Program.cs +++ b/src/samples/WorkflowCore.Sample03/Program.cs @@ -29,6 +29,17 @@ public static void Main(string[] args) host.StartWorkflow("PassingDataWorkflow", 1, initialData); + host.RegisterWorkflow>(); + //host.Start(); + + var initialData2 = new Dictionary + { + ["Value1"] = 2, + ["Value2"] = 3 + }; + + host.StartWorkflow("PassingDataWorkflow2", 1, initialData2); + Console.ReadLine(); host.Stop(); } From a1f5b732fd084fdc7ac0f0b13fecdbae6ae8735c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Thu, 23 Aug 2018 18:40:03 +0200 Subject: [PATCH 052/462] Avoid assumptions on parameters --- .../Services/FluentBuilders/ExpressionHelpers.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs b/src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs index aa78714af..ca1ba4f9a 100644 --- a/src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs +++ b/src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs @@ -31,7 +31,6 @@ public static Expression> CreateSetter( public static LambdaExpression CreateSetter(LambdaExpression getterExpression) { - var thisParameter = getterExpression.Parameters.Single(); var valueParameter = Expression.Parameter(getterExpression.ReturnType, "value"); Expression assignment; if (getterExpression.Body is MethodCallExpression callExpression && callExpression.Method.Name == "get_Item") @@ -48,7 +47,9 @@ public static LambdaExpression CreateSetter(LambdaExpression getterExpression) { assignment = Expression.Assign(getterExpression.Body, valueParameter); } - return Expression.Lambda(assignment, thisParameter, valueParameter); + + var parameters = getterExpression.Parameters.Concat(new[] {valueParameter}).ToArray(); + return Expression.Lambda(assignment, parameters); } } } From ab2fb084959dda2dac3c6fe3b966a5722590078d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Thu, 23 Aug 2018 19:09:47 +0200 Subject: [PATCH 053/462] Passed right parameters to setter. --- src/WorkflowCore/Services/WorkflowExecutor.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 06f33ac12..2574b1b19 100644 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -111,7 +111,7 @@ public async Task Execute(WorkflowInstance workflow) if (result.Proceed) { - ProcessOutputs(workflow, step, body); + ProcessOutputs(workflow, step, body, context); } _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); @@ -176,15 +176,23 @@ private void ProcessInputs(WorkflowInstance workflow, WorkflowStep step, IStepBo } } - private void ProcessOutputs(WorkflowInstance workflow, WorkflowStep step, IStepBody body) + private void ProcessOutputs(WorkflowInstance workflow, WorkflowStep step, IStepBody body, IStepExecutionContext context) { foreach (var output in step.Outputs) { var resolvedValue = output.Source.Compile().DynamicInvoke(body); var data = workflow.Data; var setter = ExpressionHelpers.CreateSetter(output.Target); - var convertedValue = Convert.ChangeType(resolvedValue, setter.Parameters[1].Type); - setter.Compile().DynamicInvoke(data, convertedValue); + var convertedValue = Convert.ChangeType(resolvedValue, setter.Parameters.Last().Type); + + if (setter.Parameters.Count == 2) + { + setter.Compile().DynamicInvoke(data, convertedValue); + } + else + { + setter.Compile().DynamicInvoke(data, context, convertedValue); + } } } From 6d849abf8fd17e66b400f3039fcd65c03eb4ef40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Thu, 23 Aug 2018 21:24:34 +0200 Subject: [PATCH 054/462] Added Tests for persistence with dynamic data --- .../Scenarios/DynamicDataIOScenario.cs | 70 +++++++++++++++++++ .../Scenarios/MongoDynamicDataScenario.cs | 16 +++++ .../Scenarios/PostgresDynamicDataScenario.cs | 16 +++++ .../Scenarios/SqlServerDynamicDataScenario.cs | 16 +++++ 4 files changed, 118 insertions(+) create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs create mode 100644 test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDynamicDataScenario.cs create mode 100644 test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDynamicDataScenario.cs create mode 100644 test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDynamicDataScenario.cs diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs new file mode 100644 index 000000000..ccfe3a568 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using FluentAssertions; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Testing; +using Xunit; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class DynamicDataIOScenario : WorkflowTest + { + public class AddNumbers : StepBody + { + public int Input1 { get; set; } + public int Input2 { get; set; } + public int Output { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Output = (Input1 + Input2); + return ExecutionResult.Next(); + } + } + + public class MyDataClass + { + public int Value1 { get; set; } + public int Value2 { get; set; } + + public Dictionary _storage = new Dictionary(); + + public int this[string propertyName] + { + get => _storage[propertyName]; + set => _storage[propertyName] = value; + } + } + + public class DataIOWorkflow : IWorkflow + { + public string Id => "DataIOWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Input(step => step.Input1, data => data.Value1) + .Input(step => step.Input2, data => data.Value2) + .Output(data => data["Value3"], step => step.Output); + } + } + + public DynamicDataIOScenario() + { + Setup(); + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(new MyDataClass() { Value1 = 2, Value2 = 3 }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + GetData(workflowId)["Value3"].Should().Be(5); + } + } +} diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDynamicDataScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDynamicDataScenario.cs new file mode 100644 index 000000000..456f0a0f1 --- /dev/null +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDynamicDataScenario.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("Mongo collection")] + public class MongoDynamicDataScenario : DynamicDataIOScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + } + } +} diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDynamicDataScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDynamicDataScenario.cs new file mode 100644 index 000000000..f654ca16b --- /dev/null +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDynamicDataScenario.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.PostgreSQL.Scenarios +{ + [Collection("Postgres collection")] + public class PostgresDynamicDataScenario : DynamicDataIOScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UsePostgreSQL(PostgresDockerSetup.ScenarioConnectionString, true, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDynamicDataScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDynamicDataScenario.cs new file mode 100644 index 000000000..34cb86215 --- /dev/null +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDynamicDataScenario.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.SqlServer.Scenarios +{ + [Collection("SqlServer collection")] + public class SqlServerDynamicDataScenario : DynamicDataIOScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseSqlServer(SqlDockerSetup.ScenarioConnectionString, true, true)); + } + } +} From 3e0899bc80c4f23bab4d209f657fd972d257ebdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Thu, 23 Aug 2018 21:29:34 +0200 Subject: [PATCH 055/462] Fixed naming and visibility of storage dictionary in dynamic data tests --- .../Scenarios/DynamicDataIOScenario.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs index ccfe3a568..7cf40010d 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs @@ -28,12 +28,12 @@ public class MyDataClass public int Value1 { get; set; } public int Value2 { get; set; } - public Dictionary _storage = new Dictionary(); + public Dictionary Storage = new Dictionary(); public int this[string propertyName] { - get => _storage[propertyName]; - set => _storage[propertyName] = value; + get => Storage[propertyName]; + set => Storage[propertyName] = value; } } From d9f023712736cd2df378d4ec7ff0f19a8c11ed49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Fri, 24 Aug 2018 20:15:35 +0200 Subject: [PATCH 056/462] Properties instead of public field --- .../Scenarios/DynamicDataIOScenario.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs index 7cf40010d..779e3f1dd 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs @@ -27,8 +27,7 @@ public class MyDataClass { public int Value1 { get; set; } public int Value2 { get; set; } - - public Dictionary Storage = new Dictionary(); + public Dictionary Storage { get; set; } = new Dictionary(); public int this[string propertyName] { From 6fafccda8f286394270f5a08176d351c094b298f Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 30 Aug 2018 21:09:14 -0700 Subject: [PATCH 057/462] bump version --- test/WorkflowCore.Testing/WorkflowCore.Testing.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index d5f73e844..df5dc1bd9 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 1.5.0 - 1.5.0.0 - 1.5.0.0 + 1.6.0 + 1.6.0.0 + 1.6.0.0 Facilitates testing of workflows built on Workflow-Core @@ -17,4 +17,4 @@ - \ No newline at end of file + From b33b86cb7201ee8687982ef0da4ae53300319854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Mon, 10 Sep 2018 18:46:40 +0200 Subject: [PATCH 058/462] Fixed indentation to match previous code --- .../PassingDataWorkflow2.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs b/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs index 482ea1229..b731d53e2 100644 --- a/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs +++ b/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs @@ -17,17 +17,17 @@ public void Build(IWorkflowBuilder> builder) return ExecutionResult.Next(); }) .Then() - .Input(step => step.Input1, data => data["Value1"]) - .Input(step => step.Input2, data => data["Value2"]) - .Output(data => data["Value3"], step => step.Output) + .Input(step => step.Input1, data => data["Value1"]) + .Input(step => step.Input2, data => data["Value2"]) + .Output(data => data["Value3"], step => step.Output) .Then() - .Name("Print custom message") - .Input(step => step.Message, data => "The answer is " + data["Value3"].ToString()) + .Name("Print custom message") + .Input(step => step.Message, data => "The answer is " + data["Value3"].ToString()) .Then(context => - { - Console.WriteLine("Workflow complete"); - return ExecutionResult.Next(); - }); + { + Console.WriteLine("Workflow complete"); + return ExecutionResult.Next(); + }); } public string Id => "PassingDataWorkflow2"; From 976a8ab69e4c2b8add4826a814b79cc38ccc0ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Mon, 10 Sep 2018 19:28:14 +0200 Subject: [PATCH 059/462] Added dynamic data test to WorkflowExecutorFixture --- .../Services/WorkflowExecutorFixture.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 1626eb814..e9048ba98 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -223,6 +223,54 @@ public void should_map_outputs() data.Value1.Should().Be(7); } + [Fact(DisplayName = "Should map dynamic outputs")] + public void should_map_outputs_dynamic() + { + //arrange + Expression> p1 = x => x.Property1; + Expression> v1 = (x, context) => x["Value1"]; + + var step1Body = A.Fake(); + A.CallTo(() => step1Body.Property1).Returns(7); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); + WorkflowStep step1 = BuildFakeStep(step1Body, new List(), new List() + { + new DataMapping() + { + Source = p1, + Target = v1 + } + } + ); + + Given1StepWorkflow(step1, "Workflow", 1); + + var data = new DynamicDataClass() + { + ["Value1"] = 5 + }; + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + Data = data, + ExecutionPointers = new List() + { + new ExecutionPointer() { Active = true, StepId = 0 } + } + }; + + //act + Subject.Execute(instance); + + //assert + data["Value1"].Should().Be(7); + } + [Fact(DisplayName = "Should handle step exception")] public void should_handle_step_exception() { @@ -333,5 +381,16 @@ public class DataClass public int Value2 { get; set; } public int Value3 { get; set; } } + + public class DynamicDataClass + { + public Dictionary Storage { get; set; } = new Dictionary(); + + public int this[string propertyName] + { + get => Storage[propertyName]; + set => Storage[propertyName] = value; + } + } } } From ee4272f7b05d4c37c6c56b428c72914065e5e5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Mon, 10 Sep 2018 19:28:53 +0200 Subject: [PATCH 060/462] Fixed spelling mistakes in DefinitionLoaderTests --- .../Services/DefinitionStorage/DefinitionLoaderTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs index 5aacbf88a..243247f7f 100644 --- a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs +++ b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs @@ -27,7 +27,7 @@ public DefinitionLoaderTests() } [Fact(DisplayName = "Should register workflow")] - public void RegisterDefintion() + public void RegisterDefinition() { _subject.LoadDefinition("{\"Id\": \"HelloWorld\", \"Version\": 1, \"Steps\": []}"); @@ -37,7 +37,7 @@ public void RegisterDefintion() } [Fact(DisplayName = "Should parse definition")] - public void ParseDefintion() + public void ParseDefinition() { _subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionJson()); From de172a015e6fe946dd03e9c221e217e658b994f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Mon, 17 Sep 2018 21:33:41 +0200 Subject: [PATCH 061/462] Added DefinitionLoader unittests for dynamic stored workflow definitions --- .../DataTypes/DynamicData.cs | 15 ++++ test/WorkflowCore.TestAssets/Utils.cs | 5 ++ .../WorkflowCore.TestAssets.csproj | 4 ++ .../stored-dynamic-definition.json | 70 +++++++++++++++++++ .../DefinitionLoaderTests.cs | 14 +++- 5 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 test/WorkflowCore.TestAssets/DataTypes/DynamicData.cs create mode 100644 test/WorkflowCore.TestAssets/stored-dynamic-definition.json diff --git a/test/WorkflowCore.TestAssets/DataTypes/DynamicData.cs b/test/WorkflowCore.TestAssets/DataTypes/DynamicData.cs new file mode 100644 index 000000000..508ecda1e --- /dev/null +++ b/test/WorkflowCore.TestAssets/DataTypes/DynamicData.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace WorkflowCore.TestAssets.DataTypes +{ + public class DynamicData + { + public Dictionary Storage { get; set; } = new Dictionary(); + + public object this[string propertyName] + { + get => Storage.TryGetValue(propertyName, out var value) ? value : null; + set => Storage[propertyName] = value; + } + } +} diff --git a/test/WorkflowCore.TestAssets/Utils.cs b/test/WorkflowCore.TestAssets/Utils.cs index 87e523494..c4f5cae5a 100644 --- a/test/WorkflowCore.TestAssets/Utils.cs +++ b/test/WorkflowCore.TestAssets/Utils.cs @@ -23,6 +23,11 @@ public static string GetTestDefinitionJson() //return Properties.Resources.ResourceManager.GetString("stored_definition"); return File.ReadAllText("stored-definition.json"); } + + public static string GetTestDefinitionDynamicJson() + { + return File.ReadAllText("stored-dynamic-definition.json"); + } } } diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index bb6e46dae..96c69f2de 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -13,9 +13,13 @@ + + + Always + Always diff --git a/test/WorkflowCore.TestAssets/stored-dynamic-definition.json b/test/WorkflowCore.TestAssets/stored-dynamic-definition.json new file mode 100644 index 000000000..dc572f8af --- /dev/null +++ b/test/WorkflowCore.TestAssets/stored-dynamic-definition.json @@ -0,0 +1,70 @@ +{ + "Id": "Test", + "Version": 1, + "Description": "", + "DataType": "WorkflowCore.TestAssets.DataTypes.DynamicData, WorkflowCore.TestAssets", + "Steps": [ + { + "Id": "Step1", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "Inputs": { "Value": "data[\"Counter1\"]" }, + "Outputs": { "Counter1": "step.Value" }, + "NextStepId": "Step2" + }, + { + "Id": "Step2", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "Inputs": { "Value": "data[\"Counter2\"]" }, + "Outputs": { "Counter2": "step.Value" }, + "NextStepId": "Step3" + }, + { + "Id": "Step3", + "StepType": "WorkflowCore.Primitives.If, WorkflowCore", + "NextStepId": "Step4", + "Inputs": { "Condition": "object.Equals(data[\"Flag1\"], true)" }, + "Do": [ + [ + { + "Id": "Step3.1.1", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "Inputs": { "Value": "data[\"Counter3\"]" }, + "Outputs": { "Counter3": "step.Value" }, + "NextStepId": "Step3.1.2" + }, + { + "Id": "Step3.1.2", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "Inputs": { "Value": "data[\"Counter4\"]" }, + "Outputs": { "Counter4": "step.Value" } + } + ], + [ + { + "Id": "Step3.2.1", + "StepType": "WorkflowCore.Primitives.WaitFor, WorkflowCore", + "NextStepId": "Step3.2.2", + "CancelCondition": "object.Equals(data[\"Flag2\"], true)", + "Inputs": { + "EventName": "\"Event1\"", + "EventKey": "\"Key1\"", + "EffectiveDate": "DateTime.Now" + } + }, + { + "Id": "Step3.2.2", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "Inputs": { "Value": "data[\"Counter5\"]" }, + "Outputs": { "Counter5": "step.Value" } + } + ] + ] + }, + { + "Id": "Step4", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "Inputs": { "Value": "data[\"Counter6\"]" }, + "Outputs": { "Counter6": "step.Value" } + } + ] +} \ No newline at end of file diff --git a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs index 243247f7f..fc92a874c 100644 --- a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs +++ b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs @@ -43,7 +43,19 @@ public void ParseDefinition() A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Id == "Test"))).MustHaveHappened(); A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Version == 1))).MustHaveHappened(); - A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.DataType == typeof(TestAssets.DataTypes.CounterBoard)))).MustHaveHappened(); + A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.DataType == typeof(CounterBoard)))).MustHaveHappened(); + A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(MatchTestDefinition, ""))).MustHaveHappened(); + } + + + [Fact(DisplayName = "Should parse definition")] + public void ParseDefinitionDynamic() + { + _subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionDynamicJson()); + + A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Id == "Test"))).MustHaveHappened(); + A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Version == 1))).MustHaveHappened(); + A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.DataType == typeof(DynamicData)))).MustHaveHappened(); A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(MatchTestDefinition, ""))).MustHaveHappened(); } From 54093675d533cce085b320d13696e29dcf8d4eb2 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 23 Sep 2018 20:45:20 -0700 Subject: [PATCH 062/462] bump versions --- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 4dc0b9883..860485dcb 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.6 - 1.6.6.0 - 1.6.6.0 + 1.6.7 + 1.6.7.0 + 1.6.7.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From ecd3d736525480b373779254f6f4c3564aa25148 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 16 Oct 2018 09:01:42 -0700 Subject: [PATCH 063/462] order of compensation steps --- ReleaseNotes/1.6.8.md | 3 + WorkflowCore.sln | 1 + .../Services/ExecutionResultProcessor.cs | 6 +- src/WorkflowCore/WorkflowCore.csproj | 6 +- .../MultistepCompensationScenario.cs | 83 +++++++++++++++++++ 5 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 ReleaseNotes/1.6.8.md create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs diff --git a/ReleaseNotes/1.6.8.md b/ReleaseNotes/1.6.8.md new file mode 100644 index 000000000..3c5781d88 --- /dev/null +++ b/ReleaseNotes/1.6.8.md @@ -0,0 +1,3 @@ +# Workflow Core 1.6.8 + +* Fixed the order in which multiple compensating steps execute within a saga transaction. [Issue 191](https://github.com/danielgerlag/workflow-core/issues/191) \ No newline at end of file diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 81f420925..d66756fe3 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -92,6 +92,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.4.0.md = ReleaseNotes\1.4.0.md ReleaseNotes\1.6.0.md = ReleaseNotes\1.6.0.md ReleaseNotes\1.6.6.md = ReleaseNotes\1.6.6.md + ReleaseNotes\1.6.8.md = ReleaseNotes\1.6.8.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index 8dc946ae8..f27d21bd5 100644 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -152,7 +152,11 @@ private void Compensate(WorkflowInstance workflow, WorkflowDefinition def, Execu if (revert) { - var prevSiblings = workflow.ExecutionPointers.Where(x => pointer.Scope.SequenceEqual(x.Scope) && x.Id != pointer.Id && x.Status == PointerStatus.Complete).ToList(); + var prevSiblings = workflow.ExecutionPointers + .Where(x => pointer.Scope.SequenceEqual(x.Scope) && x.Id != pointer.Id && x.Status == PointerStatus.Complete) + .OrderByDescending(x => x.EndTime) + .ToList(); + foreach (var siblingPointer in prevSiblings) { var siblingStep = def.Steps.First(x => x.Id == siblingPointer.StepId); diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 860485dcb..471edc85c 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.7 - 1.6.7.0 - 1.6.7.0 + 1.6.8 + 1.6.8.0 + 1.6.8.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs new file mode 100644 index 000000000..68fedfae9 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using System.Linq; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class MultistepCompensationScenario : WorkflowTest + { + public class Workflow : IWorkflow + { + public static int Compensation1Fired = -1; + public static int Compensation2Fired = -1; + public static int Compensation3Fired = -1; + public static int Compensation4Fired = -1; + public static int CompensationCounter = 0; + + public string Id => "CompensatingWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .Saga(x => x + .StartWith(context => ExecutionResult.Next()) + .CompensateWith(context => + { + CompensationCounter++; + Compensation1Fired = CompensationCounter; + }) + .Then(context => ExecutionResult.Next()) + .CompensateWith(context => + { + CompensationCounter++; + Compensation2Fired = CompensationCounter; + }) + .Then(context => ExecutionResult.Next()) + .CompensateWith(context => + { + CompensationCounter++; + Compensation3Fired = CompensationCounter; + }) + .Then(context => throw new Exception()) + .CompensateWith(context => + { + CompensationCounter++; + Compensation4Fired = CompensationCounter; + }) + ); + } + } + + public MultistepCompensationScenario() + { + Setup(); + Workflow.Compensation1Fired = -1; + Workflow.Compensation2Fired = -1; + Workflow.Compensation3Fired = -1; + Workflow.Compensation4Fired = -1; + Workflow.CompensationCounter = 0; + } + + [Fact] + public void MultiCompensationStepOrder() + { + var workflowId = StartWorkflow(null); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(1); + + Workflow.Compensation1Fired.Should().Be(4); + Workflow.Compensation2Fired.Should().Be(3); + Workflow.Compensation3Fired.Should().Be(2); + Workflow.Compensation4Fired.Should().Be(1); + } + } +} From fd3305c03d6b496314d5044854b61163ad033d8c Mon Sep 17 00:00:00 2001 From: wswind <18399096+wswind@users.noreply.github.com> Date: Thu, 15 Nov 2018 19:18:34 +0800 Subject: [PATCH 064/462] README.md url fix --- src/providers/WorkflowCore.Persistence.SqlServer/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/README.md b/src/providers/WorkflowCore.Persistence.SqlServer/README.md index 77c4ce347..a113eb894 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/README.md +++ b/src/providers/WorkflowCore.Persistence.SqlServer/README.md @@ -1,6 +1,6 @@ # SQL Server Persistence provider for Workflow Core -Provides support to persist workflows running on [Workflow Core](../../README.md) to a SQL Server database. +Provides support to persist workflows running on [Workflow Core](../../../README.md) to a SQL Server database. ## Installing From 8d37bcdc988440bb10f51f31cccdd586ecce2b4f Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 9 Dec 2018 14:29:15 -0800 Subject: [PATCH 065/462] AWS Simple Queue Service (#207) AWS SQS provider --- WorkflowCore.sln | 7 ++ .../WorkflowCore.Providers.AWS/README.md | 21 +++++ .../ServiceCollectionExtensions.cs | 18 ++++ .../Services/SQSQueueProvider.cs | 88 +++++++++++++++++++ .../WorkflowCore.Providers.AWS.csproj | 25 ++++++ .../Services/AzureStorageQueueProvider.cs | 4 +- src/samples/WorkflowCore.Sample04/Program.cs | 5 +- .../WorkflowCore.Sample04.csproj | 2 + test/ScratchPad/HelloWorld.json | 51 +---------- .../stored-definition.json | 1 + 10 files changed, 168 insertions(+), 54 deletions(-) create mode 100644 src/providers/WorkflowCore.Providers.AWS/README.md create mode 100644 src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs create mode 100644 src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs create mode 100644 src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj diff --git a/WorkflowCore.sln b/WorkflowCore.sln index d66756fe3..0e2d227fb 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -113,6 +113,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample17", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.QueueProviders.SqlServer", "src\providers\WorkflowCore.QueueProviders.SqlServer\WorkflowCore.QueueProviders.SqlServer.csproj", "{7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Providers.AWS", "src\providers\WorkflowCore.Providers.AWS\WorkflowCore.Providers.AWS.csproj", "{5E82A137-0954-46A1-8C46-13C00F0E4842}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -283,6 +285,10 @@ Global {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Debug|Any CPU.Build.0 = Debug|Any CPU {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Release|Any CPU.ActiveCfg = Release|Any CPU {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}.Release|Any CPU.Build.0 = Release|Any CPU + {5E82A137-0954-46A1-8C46-13C00F0E4842}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E82A137-0954-46A1-8C46-13C00F0E4842}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E82A137-0954-46A1-8C46-13C00F0E4842}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E82A137-0954-46A1-8C46-13C00F0E4842}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -332,6 +338,7 @@ Global {6396453F-4D0E-4CD4-BC89-87E8970F2A80} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {42F475BC-95F4-42E1-8CCD-7B9C27487E33} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} + {5E82A137-0954-46A1-8C46-13C00F0E4842} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/providers/WorkflowCore.Providers.AWS/README.md b/src/providers/WorkflowCore.Providers.AWS/README.md new file mode 100644 index 000000000..c54ba5eff --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/README.md @@ -0,0 +1,21 @@ +# AWS providers for Workflow Core + +* Provides Queueing support on [Workflow Core](../../README.md) using AWS Simple Queue Service. + +This makes it possible to have a cluster of nodes processing your workflows. + +## Installing + +Install the NuGet package "WorkflowCore.Providers.AWS" + +``` +PM> Install-Package WorkflowCore.Providers.AWS +``` + +## Usage + +Use the .UseAwsSimpleQueueService extension method when building your service provider. + +```C# +services.AddWorkflow(x => x.UseAwsSimpleQueueService(awsCredentials, amazonSQSConfig)); +``` \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..e030c0d6c --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs @@ -0,0 +1,18 @@ +using System; +using Amazon.Runtime; +using Amazon.SQS; +using Microsoft.Extensions.Logging; +using WorkflowCore.Models; +using WorkflowCore.Providers.AWS.Services; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + public static WorkflowOptions UseAwsSimpleQueueService(this WorkflowOptions options, AWSCredentials credentials, AmazonSQSConfig config) + { + options.UseQueueProvider(sp => new SQSQueueProvider(credentials, config, sp.GetService())); + return options; + } + } +} diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs new file mode 100644 index 000000000..6d52de105 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs @@ -0,0 +1,88 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Amazon.Runtime; +using Amazon.SQS; +using Amazon.SQS.Model; +using WorkflowCore.Interface; + +namespace WorkflowCore.Providers.AWS.Services +{ + public class SQSQueueProvider : IQueueProvider + { + private const int WaitTime = 5; + private readonly ILogger _logger; + private readonly IAmazonSQS _client; + private string _workflowQueue; + private string _eventQueue; + + public bool IsDequeueBlocking => true; + + public SQSQueueProvider(AWSCredentials credentials, AmazonSQSConfig config, ILoggerFactory logFactory) + { + _logger = logFactory.CreateLogger(); + _client = new AmazonSQSClient(credentials, config); + } + + public async Task QueueWork(string id, QueueType queue) + { + var queueUrl = string.Empty; + switch (queue) + { + case QueueType.Workflow: + queueUrl = _workflowQueue; + break; + case QueueType.Event: + queueUrl = _eventQueue; + break; + } + + await _client.SendMessageAsync(new SendMessageRequest(queueUrl, id)); + } + + public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) + { + var queueUrl = string.Empty; + switch (queue) + { + case QueueType.Workflow: + queueUrl = _workflowQueue; + break; + case QueueType.Event: + queueUrl = _eventQueue; + break; + } + + var result = await _client.ReceiveMessageAsync(new ReceiveMessageRequest(queueUrl) + { + MaxNumberOfMessages = 1, + WaitTimeSeconds = WaitTime + }); + + if (result.Messages.Count == 0) + return null; + + var msg = result.Messages.First(); + + await _client.DeleteMessageAsync(new DeleteMessageRequest(queueUrl, msg.ReceiptHandle)); + return msg.Body; + } + + public async Task Start() + { + var workflowQueue = await _client.CreateQueueAsync(new CreateQueueRequest("workflowcore-workflows")); + var eventQueue = await _client.CreateQueueAsync(new CreateQueueRequest("workflowcore-events")); + + _workflowQueue = workflowQueue.QueueUrl; + _eventQueue = eventQueue.QueueUrl; + } + + public Task Stop() => Task.CompletedTask; + + public void Dispose() + { + } + } +} diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj new file mode 100644 index 000000000..1acb22afb --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -0,0 +1,25 @@ + + + + netstandard2.0 + Daniel Gerlag + AWS providers for Workflow Core + +- Provides Queueing support on Workflow Core + https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md + https://github.com/danielgerlag/workflow-core + https://github.com/danielgerlag/workflow-core.git + git + 1.6.0 + + + + + + + + + + + + diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs index 75be01fd1..4c297a8ee 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs @@ -74,9 +74,7 @@ public async Task Start() await _eventQueue.CreateIfNotExistsAsync(); } - public async Task Stop() - { - } + public Task Stop() => Task.CompletedTask; public void Dispose() { diff --git a/src/samples/WorkflowCore.Sample04/Program.cs b/src/samples/WorkflowCore.Sample04/Program.cs index 8ff01a463..9967140bb 100644 --- a/src/samples/WorkflowCore.Sample04/Program.cs +++ b/src/samples/WorkflowCore.Sample04/Program.cs @@ -1,12 +1,13 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MongoDB.Driver; -//using RabbitMQ.Client; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Amazon; +using Amazon.Runtime; using WorkflowCore.Interface; using WorkflowCore.Persistence.MongoDB.Services; using WorkflowCore.Services; @@ -40,7 +41,7 @@ private static IServiceProvider ConfigureServices() //setup dependency injection IServiceCollection services = new ServiceCollection(); services.AddLogging(); - services.AddWorkflow(); + services.AddWorkflow(); //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); //services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); //services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;", true, true)); diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index df6381285..00de2607f 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -15,6 +15,7 @@ + @@ -22,6 +23,7 @@ + diff --git a/test/ScratchPad/HelloWorld.json b/test/ScratchPad/HelloWorld.json index 257cddcdc..596db504e 100644 --- a/test/ScratchPad/HelloWorld.json +++ b/test/ScratchPad/HelloWorld.json @@ -18,56 +18,9 @@ { "Id": "Print", "StepType": "ScratchPad.PrintMessage, ScratchPad", - "NextStepId": "saga", - "Inputs": { "Message": "data.Value3 + \" - \" + DateTime.Now.ToString()" } - }, - - { - "Id": "saga", - "StepType": "WorkflowCore.Primitives.Sequence, WorkflowCore", "NextStepId": "Bye", - "Saga": true, - "Do": [ - [ - { - "Id": "do1", - "StepType": "ScratchPad.PrintMessage, ScratchPad", - "NextStepId": "do2", - "Inputs": { "Message": "\"inner 1\"" }, - "CompensateWith": [ - { - "Id": "comp0", - "StepType": "ScratchPad.PrintMessage, ScratchPad", - "Inputs": { "Message": "\"undoing do1\"" } - } - ] - }, - { - "Id": "do2", - "StepType": "ScratchPad.Throw, ScratchPad", - "NextStepId": "do3", - "CompensateWith": [ - { - "Id": "comp1", - "NextStepId": "comp2", - "StepType": "ScratchPad.PrintMessage, ScratchPad", - "Inputs": { "Message": "\"undoing do2\"" } - }, - { - "Id": "comp2", - "StepType": "ScratchPad.PrintMessage, ScratchPad", - "Inputs": { "Message": "\"still undoing do2\"" } - } - ] - }, - { - "Id": "do3", - "StepType": "ScratchPad.PrintMessage, ScratchPad", - "Inputs": { "Message": "\"inner 3\"" } - } - ] - ] - }, + "Inputs": { "Message": "data.Value3 + \" - \" + DateTime.Now.ToString()" } + }, { "Id": "Bye", diff --git a/test/WorkflowCore.TestAssets/stored-definition.json b/test/WorkflowCore.TestAssets/stored-definition.json index 1b131d42f..e60857fe0 100644 --- a/test/WorkflowCore.TestAssets/stored-definition.json +++ b/test/WorkflowCore.TestAssets/stored-definition.json @@ -7,6 +7,7 @@ { "Id": "Step1", "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "ErrorBehavior": "Retry", "Inputs": { "Value": "data.Counter1" }, "Outputs": { "Counter1": "step.Value" }, "NextStepId": "Step2" From 5a7f7bb7a6a03878fc57f7d98f5b5d9b4d6c608b Mon Sep 17 00:00:00 2001 From: erolkskn Date: Mon, 10 Dec 2018 03:33:51 +0300 Subject: [PATCH 066/462] Add MySQL Persistence Provider --- WorkflowCore.sln | 16 ++++- .../MysqlContext.cs | 64 +++++++++++++++++++ .../MysqlContextFactory.cs | 24 +++++++ .../WorkflowCore.Persistence.MySQL/README.md | 19 ++++++ .../ServiceCollectionExtensions.cs | 17 +++++ .../WorkflowCore.Persistence.MySQL.csproj | 20 ++++++ test/WorkflowCore.Tests.MySQL/DockerSetup.cs | 51 +++++++++++++++ .../MysqlPersistenceProviderFixture.cs | 23 +++++++ .../Scenarios/MysqlBasicScenario.cs | 15 +++++ .../Scenarios/MysqlDataScenario.cs | 15 +++++ .../Scenarios/MysqlDynamicDataScenario.cs | 17 +++++ .../Scenarios/MysqlEventScenario.cs | 19 ++++++ .../Scenarios/MysqlForeachScenario.cs | 19 ++++++ .../Scenarios/MysqlForkScenario.cs | 19 ++++++ .../Scenarios/MysqlIfScenario.cs | 19 ++++++ .../Scenarios/MysqlRetrySagaScenario.cs | 19 ++++++ .../Scenarios/MysqlSagaScenario.cs | 19 ++++++ .../Scenarios/MysqlUserScenario.cs | 19 ++++++ .../Scenarios/MysqlWhenScenario.cs | 19 ++++++ .../Scenarios/MysqlWhileScenario.cs | 19 ++++++ .../WorkflowCore.Tests.MySQL.csproj | 25 ++++++++ 21 files changed, 476 insertions(+), 1 deletion(-) create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/MysqlContextFactory.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/README.md create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj create mode 100644 test/WorkflowCore.Tests.MySQL/DockerSetup.cs create mode 100644 test/WorkflowCore.Tests.MySQL/MysqlPersistenceProviderFixture.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlBasicScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDataScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs create mode 100644 test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 0e2d227fb..d418c890b 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -113,7 +113,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample17", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.QueueProviders.SqlServer", "src\providers\WorkflowCore.QueueProviders.SqlServer\WorkflowCore.QueueProviders.SqlServer.csproj", "{7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Providers.AWS", "src\providers\WorkflowCore.Providers.AWS\WorkflowCore.Providers.AWS.csproj", "{5E82A137-0954-46A1-8C46-13C00F0E4842}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Providers.AWS", "src\providers\WorkflowCore.Providers.AWS\WorkflowCore.Providers.AWS.csproj", "{5E82A137-0954-46A1-8C46-13C00F0E4842}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Persistence.MySQL", "src\providers\WorkflowCore.Persistence.MySQL\WorkflowCore.Persistence.MySQL.csproj", "{453E260D-DBDC-4DDC-BC9C-CA500CED7897}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.MySQL", "test\WorkflowCore.Tests.MySQL\WorkflowCore.Tests.MySQL.csproj", "{DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -289,6 +293,14 @@ Global {5E82A137-0954-46A1-8C46-13C00F0E4842}.Debug|Any CPU.Build.0 = Debug|Any CPU {5E82A137-0954-46A1-8C46-13C00F0E4842}.Release|Any CPU.ActiveCfg = Release|Any CPU {5E82A137-0954-46A1-8C46-13C00F0E4842}.Release|Any CPU.Build.0 = Release|Any CPU + {453E260D-DBDC-4DDC-BC9C-CA500CED7897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {453E260D-DBDC-4DDC-BC9C-CA500CED7897}.Debug|Any CPU.Build.0 = Debug|Any CPU + {453E260D-DBDC-4DDC-BC9C-CA500CED7897}.Release|Any CPU.ActiveCfg = Release|Any CPU + {453E260D-DBDC-4DDC-BC9C-CA500CED7897}.Release|Any CPU.Build.0 = Release|Any CPU + {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -339,6 +351,8 @@ Global {42F475BC-95F4-42E1-8CCD-7B9C27487E33} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {5E82A137-0954-46A1-8C46-13C00F0E4842} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} + {453E260D-DBDC-4DDC-BC9C-CA500CED7897} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} + {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs new file mode 100644 index 000000000..06c1e7ab5 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs @@ -0,0 +1,64 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WorkflowCore.Persistence.EntityFramework.Models; +using WorkflowCore.Persistence.EntityFramework.Services; + +namespace WorkflowCore.Persistence.MySQL +{ + public class MysqlContext : WorkflowDbContext + { + private readonly string _connectionString; + private readonly Action _mysqlOptionsAction; + + public MysqlContext(string connectionString, Action mysqlOptionsAction = null) + : base() + { + _connectionString = connectionString; + _mysqlOptionsAction = mysqlOptionsAction; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + optionsBuilder.UseMySql(_connectionString, _mysqlOptionsAction); + } + + protected override void ConfigureSubscriptionStorage(EntityTypeBuilder builder) + { + builder.ToTable("Subscription"); + builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); + } + + protected override void ConfigureWorkflowStorage(EntityTypeBuilder builder) + { + builder.ToTable("Workflow"); + builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); + } + + protected override void ConfigureExecutionPointerStorage(EntityTypeBuilder builder) + { + builder.ToTable("ExecutionPointer"); + builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); + } + + protected override void ConfigureExecutionErrorStorage(EntityTypeBuilder builder) + { + builder.ToTable("ExecutionError"); + builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); + } + + protected override void ConfigureExetensionAttributeStorage(EntityTypeBuilder builder) + { + builder.ToTable("ExtensionAttribute"); + builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); + } + + protected override void ConfigureEventStorage(EntityTypeBuilder builder) + { + builder.ToTable("Event"); + builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/MysqlContextFactory.cs b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContextFactory.cs new file mode 100644 index 000000000..428f0eeac --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContextFactory.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using System; +using WorkflowCore.Persistence.EntityFramework.Interfaces; +using WorkflowCore.Persistence.EntityFramework.Services; + +namespace WorkflowCore.Persistence.MySQL +{ + public class MysqlContextFactory : IWorkflowDbContextFactory + { + private readonly string _connectionString; + private readonly Action _mysqlOptionsAction; + + public MysqlContextFactory(string connectionString, Action mysqlOptionsAction = null) + { + _connectionString = connectionString; + _mysqlOptionsAction = mysqlOptionsAction; + } + + public WorkflowDbContext Build() + { + return new MysqlContext(_connectionString, _mysqlOptionsAction); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/README.md b/src/providers/WorkflowCore.Persistence.MySQL/README.md new file mode 100644 index 000000000..f24df77e1 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/README.md @@ -0,0 +1,19 @@ +# MySQL Persistence provider for Workflow Core + +Provides support to persist workflows running on [Workflow Core](../../README.md) to a MySQL database. + +## Installing + +Install the NuGet package "WorkflowCore.Persistence.MySQL" + +``` +PM> Install-Package WorkflowCore.Persistence.MySQL -Pre +``` + +## Usage + +Use the .UseMySQL extension method when building your service provider. + +```C# +services.AddWorkflow(x => x.UseMySQL(@"Server=127.0.0.1;Database=workflow;User=root;Password=password;", true)); +``` diff --git a/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..28c22bd88 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs @@ -0,0 +1,17 @@ +using System; +using Microsoft.EntityFrameworkCore.Infrastructure; +using WorkflowCore.Models; +using WorkflowCore.Persistence.EntityFramework.Services; +using WorkflowCore.Persistence.MySQL; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + public static WorkflowOptions UseMySQL(this WorkflowOptions options, string connectionString, bool canCreateDB, Action mysqlOptionsAction = null) + { + options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new MysqlContextFactory(connectionString, mysqlOptionsAction), canCreateDB, false)); + return options; + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj new file mode 100644 index 000000000..35bbbce09 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + diff --git a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs new file mode 100644 index 000000000..9abe7fbf2 --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using Docker.Testify; +using Xunit; +using MySql.Data.MySqlClient; +using System; + +namespace WorkflowCore.Tests.MySQL +{ + public class MysqlDockerSetup : DockerSetup + { + public static string ConnectionString { get; set; } + public static string ScenarioConnectionString { get; set; } + public static string RootPassword => "rootpwd123"; + + public override string ImageTag => "5.7.24"; + public override string ImageName => "mysql"; + public override IList EnvironmentVariables => new List { + $"MYSQL_ROOT_PASSWORD={RootPassword}" + }; + + public override int InternalPort => 3306; + + public override void PublishConnectionInfo() + { + ConnectionString = $"Server=127.0.0.1;Port={ExternalPort};Database=workflow;User=root;Password={RootPassword};"; + ScenarioConnectionString = $"Server=127.0.0.1;Port={ExternalPort};Database=scenarios;User=root;Password={RootPassword};"; + } + + public override bool TestReady() + { + try + { + var connection = new MySqlConnection($"host=127.0.0.1;port={ExternalPort};user=root;password={RootPassword};database=mysql;"); + connection.Open(); + connection.Close(); + return true; + } + catch + { + return false; + } + + } + } + + [CollectionDefinition("Mysql collection")] + public class MysqlCollection : ICollectionFixture + { + } + +} diff --git a/test/WorkflowCore.Tests.MySQL/MysqlPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.MySQL/MysqlPersistenceProviderFixture.cs new file mode 100644 index 000000000..ffdd71e2b --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/MysqlPersistenceProviderFixture.cs @@ -0,0 +1,23 @@ +using WorkflowCore.Interface; +using WorkflowCore.Persistence.EntityFramework.Services; +using WorkflowCore.Persistence.MySQL; +using WorkflowCore.UnitTests; +using Xunit; +using Xunit.Abstractions; + +namespace WorkflowCore.Tests.MySQL +{ + [Collection("Mysql collection")] + public class MysqlPersistenceProviderFixture : BasePersistenceFixture + { + private readonly IPersistenceProvider _subject; + protected override IPersistenceProvider Subject => _subject; + + public MysqlPersistenceProviderFixture(MysqlDockerSetup dockerSetup, ITestOutputHelper output) + { + output.WriteLine($"Connecting on {MysqlDockerSetup.ConnectionString}"); + _subject = new EntityFrameworkPersistenceProvider(new MysqlContextFactory(MysqlDockerSetup.ConnectionString), true, false); + _subject.EnsureStoreExists(); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlBasicScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlBasicScenario.cs new file mode 100644 index 000000000..8282b00f6 --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlBasicScenario.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlBasicScenario : BasicScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDataScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDataScenario.cs new file mode 100644 index 000000000..d64666373 --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDataScenario.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlDataScenario : DataIOScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs new file mode 100644 index 000000000..4b8065dc1 --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs @@ -0,0 +1,17 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.MySQL; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlDynamicDataScenario : DynamicDataIOScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs new file mode 100644 index 000000000..3fbe0ca99 --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.MySQL; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlEventScenario : EventScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs new file mode 100644 index 000000000..2c4cb29a6 --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.MySQL; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlForeachScenario : ForeachScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs new file mode 100644 index 000000000..d706c68d8 --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.MySQL; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlForkScenario : ForkScenario + { + protected override void Configure(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs new file mode 100644 index 000000000..e2fea97a6 --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.MySQL; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlIfScenario : IfScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs new file mode 100644 index 000000000..41c26ed88 --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.MySQL; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlRetrySagaScenario : RetrySagaScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs new file mode 100644 index 000000000..f540a746b --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.MySQL; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlSagaScenario : SagaScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs new file mode 100644 index 000000000..297930c1b --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.MySQL; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlUserScenario : UserScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs new file mode 100644 index 000000000..23ed6c79d --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.MySQL; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlWhenScenario : WhenScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs new file mode 100644 index 000000000..dc127b6e5 --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.MySQL; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlWhileScenario : WhileScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj new file mode 100644 index 000000000..9a7efe2ff --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj @@ -0,0 +1,25 @@ + + + + netcoreapp2.0 + + false + + + + + + + + + + + + + + + + + + + From 78eb877a769a659a3a78aaae9d909dc9b6ca045f Mon Sep 17 00:00:00 2001 From: erolkskn Date: Mon, 10 Dec 2018 04:03:18 +0300 Subject: [PATCH 067/462] Codacy Quality Review Commit --- src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs index 06c1e7ab5..dbf4a8085 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs @@ -13,7 +13,6 @@ public class MysqlContext : WorkflowDbContext private readonly Action _mysqlOptionsAction; public MysqlContext(string connectionString, Action mysqlOptionsAction = null) - : base() { _connectionString = connectionString; _mysqlOptionsAction = mysqlOptionsAction; From 18a7642ca1813791f9387d005355ad14de64b449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Tue, 11 Dec 2018 21:02:15 +0100 Subject: [PATCH 068/462] Fixed problem were GetDefinition without version would throw if now workflow is found --- src/WorkflowCore/Services/WorkflowRegistry.cs | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowRegistry.cs b/src/WorkflowCore/Services/WorkflowRegistry.cs index a16a3cd2c..024983353 100644 --- a/src/WorkflowCore/Services/WorkflowRegistry.cs +++ b/src/WorkflowCore/Services/WorkflowRegistry.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -21,24 +22,14 @@ public WorkflowDefinition GetDefinition(string workflowId, int? version = null) if (version.HasValue) { var entry = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version.Value); - if (entry == null) - { - return null; - } - - // TODO: What in the heack does Item3 mean? - return entry.Item3; + // TODO: What in the heck does Item3 mean? + return entry?.Item3; } else { - int maxVersion = _registry.Where(x => x.Item1 == workflowId).Max(x => x.Item2); - var entry = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == maxVersion); - if (entry == null) - { - return null; - } - - return entry.Item3; + var entry = _registry.Where(x => x.Item1 == workflowId).OrderByDescending(x => x.Item2) + .FirstOrDefault(); + return entry?.Item3; } } @@ -49,10 +40,10 @@ public void RegisterWorkflow(IWorkflow workflow) throw new InvalidOperationException($"Workflow {workflow.Id} version {workflow.Version} is already registered"); } - var builder = (_serviceProvider.GetService(typeof(IWorkflowBuilder)) as IWorkflowBuilder).UseData(); + var builder = _serviceProvider.GetService().UseData(); workflow.Build(builder); var def = builder.Build(workflow.Id, workflow.Version); - _registry.Add(new Tuple(workflow.Id, workflow.Version, def)); + _registry.Add(Tuple.Create(workflow.Id, workflow.Version, def)); } public void RegisterWorkflow(WorkflowDefinition definition) @@ -62,7 +53,7 @@ public void RegisterWorkflow(WorkflowDefinition definition) throw new InvalidOperationException($"Workflow {definition.Id} version {definition.Version} is already registered"); } - _registry.Add(new Tuple(definition.Id, definition.Version, definition)); + _registry.Add(Tuple.Create(definition.Id, definition.Version, definition)); } public void RegisterWorkflow(IWorkflow workflow) @@ -70,13 +61,13 @@ public void RegisterWorkflow(IWorkflow workflow) { if (_registry.Any(x => x.Item1 == workflow.Id && x.Item2 == workflow.Version)) { - throw new InvalidOperationException($"Workflow {workflow.Id} version {workflow.Version} is already registed"); + throw new InvalidOperationException($"Workflow {workflow.Id} version {workflow.Version} is already registered"); } - var builder = (_serviceProvider.GetService(typeof(IWorkflowBuilder)) as IWorkflowBuilder).UseData(); + var builder = _serviceProvider.GetService().UseData(); workflow.Build(builder); var def = builder.Build(workflow.Id, workflow.Version); - _registry.Add(new Tuple(workflow.Id, workflow.Version, def)); + _registry.Add(Tuple.Create(workflow.Id, workflow.Version, def)); } } } From 4bfba498e1a41517d833894be54f7487d24af344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Gr=C3=BCner?= Date: Tue, 11 Dec 2018 21:16:38 +0100 Subject: [PATCH 069/462] Added environments to MemoryPersistanceProvider for parallel testing --- .../MemoryPersistenceProvider.cs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs index 582562281..26c14153a 100644 --- a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -14,11 +15,26 @@ namespace WorkflowCore.Services /// public class MemoryPersistenceProvider : IPersistenceProvider { + private static readonly ConcurrentDictionary, List, List, List>> Environments = + new ConcurrentDictionary, List, List, List>>(); + private readonly List _instances; + private readonly List _subscriptions; + private readonly List _events; + private readonly List _errors; + + public MemoryPersistenceProvider() + : this("") + { + } - private static List _instances = new List(); - private static List _subscriptions = new List(); - private static List _events = new List(); - private static List _errors = new List(); + public MemoryPersistenceProvider(string environmentKey) + { + var environment = Environments.GetOrAdd(environmentKey, _ => Tuple.Create(new List(), new List(), new List(), new List())); + _instances = environment.Item1; + _subscriptions = environment.Item2; + _events = environment.Item3; + _errors = environment.Item4; + } public async Task CreateNewWorkflow(WorkflowInstance workflow) { From 7d1001694fd21b50100dbbeaf8af566ede18ddeb Mon Sep 17 00:00:00 2001 From: BEat Date: Fri, 14 Dec 2018 20:13:25 +0100 Subject: [PATCH 070/462] Updated FakeItEasy to avoid problems with new versions of .NetCore --- test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index a3a91b2fc..b00d948ea 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -16,7 +16,7 @@ - + From f68d049e4c81cf5ab70dc882dc4477f7a7d666b7 Mon Sep 17 00:00:00 2001 From: BEat Date: Fri, 14 Dec 2018 20:14:05 +0100 Subject: [PATCH 071/462] Fixed bug with Converter.ChangeType being called on object type --- src/WorkflowCore/Services/WorkflowExecutor.cs | 10 +++- .../Services/WorkflowExecutorFixture.cs | 56 ++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 2574b1b19..c0ae1278f 100644 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -183,7 +183,15 @@ private void ProcessOutputs(WorkflowInstance workflow, WorkflowStep step, IStepB var resolvedValue = output.Source.Compile().DynamicInvoke(body); var data = workflow.Data; var setter = ExpressionHelpers.CreateSetter(output.Target); - var convertedValue = Convert.ChangeType(resolvedValue, setter.Parameters.Last().Type); + var targetType = setter.Parameters.Last().Type; + + var convertedValue = resolvedValue; + // We need to make sure the resolvedValue is of the correct type. + // However if the targetType is object we don't need to do anything and in some cases Convert.ChangeType will throw. + if (targetType != typeof(object)) + { + convertedValue = Convert.ChangeType(resolvedValue, targetType); + } if (setter.Parameters.Count == 2) { diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index e9048ba98..768a1fc23 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -271,6 +271,58 @@ public void should_map_outputs_dynamic() data["Value1"].Should().Be(7); } + /// + /// This test verifies that storing an object that does not implement IConvertable, in a step variable of type object works. + /// The problem is that calling for example Convert.ChangeType(new DataClass(), typeof(object)) throws, even though the convertion should be trivial. + /// + [Fact(DisplayName = "Should map object outputs, without calling Convert.ChangeType")] + public void should_map_outputs_object() + { + //arrange + Expression> p1 = x => x.Property4; + Expression> v1 = (x, context) => x.Value4; + + var step1Body = A.Fake(); + A.CallTo(() => step1Body.Property4).Returns(new DataClass()); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); + WorkflowStep step1 = BuildFakeStep(step1Body, new List(), new List() + { + new DataMapping() + { + Source = p1, + Target = v1 + } + } + ); + + Given1StepWorkflow(step1, "Workflow", 1); + + var data = new DataClass() + { + Value4 = 4 + }; + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + Data = data, + ExecutionPointers = new List() + { + new ExecutionPointer() { Active = true, StepId = 0 } + } + }; + + //act + Subject.Execute(instance); + + //assert + data.Value4.Should().BeOfType(); + } + [Fact(DisplayName = "Should handle step exception")] public void should_handle_step_exception() { @@ -372,7 +424,8 @@ public interface IStepWithProperties : IStepBody { int Property1 { get; set; } int Property2 { get; set; } - int Property3 { get; set; } + int Property3 { get; set; } + DataClass Property4 { get; set; } } public class DataClass @@ -380,6 +433,7 @@ public class DataClass public int Value1 { get; set; } public int Value2 { get; set; } public int Value3 { get; set; } + public object Value4 { get; set; } } public class DynamicDataClass From f455477cea908c6e4284724b0e5ca7c22299b44c Mon Sep 17 00:00:00 2001 From: BEat Date: Sat, 15 Dec 2018 21:32:10 +0100 Subject: [PATCH 072/462] Added Unittests for WorkflowRegistry --- .../Services/WorkflowRegistryFixture.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 test/WorkflowCore.UnitTests/Services/WorkflowRegistryFixture.cs diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowRegistryFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowRegistryFixture.cs new file mode 100644 index 000000000..c3dbb96fd --- /dev/null +++ b/test/WorkflowCore.UnitTests/Services/WorkflowRegistryFixture.cs @@ -0,0 +1,64 @@ +using FakeItEasy; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Services; +using FluentAssertions; +using Xunit; +using WorkflowCore.Primitives; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace WorkflowCore.UnitTests.Services +{ + public class WorkflowRegistryFixture + { + protected IServiceProvider ServiceProvider { get; } + protected WorkflowRegistry Subject { get; } + protected WorkflowDefinition Definition { get; } + + public WorkflowRegistryFixture() + { + ServiceProvider = A.Fake(); + Subject = new WorkflowRegistry(ServiceProvider); + + Definition = new WorkflowDefinition{ + Id = "TestWorkflow", + Version = 1, + }; + Subject.RegisterWorkflow(Definition); + } + + [Fact(DisplayName = "Should return existing workflow")] + public void getdefinition_should_return_existing_workflow() + { + Subject.GetDefinition(Definition.Id).Should().Be(Definition); + Subject.GetDefinition(Definition.Id, Definition.Version).Should().Be(Definition); + } + + [Fact(DisplayName = "Should return null on unknown workflow")] + public void getdefinition_should_return_null_on_unknown() + { + Subject.GetDefinition("UnkownWorkflow").Should().BeNull(); + Subject.GetDefinition("UnkownWorkflow", 1).Should().BeNull(); + } + + [Fact(DisplayName = "Should return highest version of existing workflow")] + public void getdefinition_should_return_highest_version_workflow() + { + var definition2 = new WorkflowDefinition{ + Id = Definition.Id, + Version = Definition.Version + 1, + }; + Subject.RegisterWorkflow(definition2); + + Subject.GetDefinition(Definition.Id).Should().Be(definition2); + Subject.GetDefinition(Definition.Id, definition2.Version).Should().Be(definition2); + Subject.GetDefinition(Definition.Id, Definition.Version).Should().Be(Definition); + } + } +} \ No newline at end of file From 6908b0057689cba2c3d374f1a549cb2714230c7c Mon Sep 17 00:00:00 2001 From: erolkskn Date: Thu, 27 Dec 2018 05:10:42 +0300 Subject: [PATCH 073/462] Increment EF Core version to 2.1.0 from 2.0.0 --- ...orkflowCore.Persistence.EntityFramework.csproj | 4 ++-- .../WorkflowCore.Persistence.MySQL.csproj | 4 ++-- .../20170126230815_InitialDatabase.Designer.cs | 2 +- .../Migrations/20170126230815_InitialDatabase.cs | 2 +- .../Migrations/20170312161610_Events.Designer.cs | 2 +- .../Migrations/20170312161610_Events.cs | 2 +- .../20170507214430_ControlStructures.Designer.cs | 2 +- .../20170519231452_PersistOutcome.Designer.cs | 2 +- .../20170722200412_WfReference.Designer.cs | 2 +- .../20171223020844_StepScope.Designer.cs | 2 +- .../PostgresPersistenceProviderModelSnapshot.cs | 2 +- .../WorkflowCore.Persistence.PostgreSQL.csproj | 8 ++++---- .../WorkflowCore.Persistence.SqlServer.csproj | 6 +++--- .../WorkflowCore.Sample01.csproj | 15 +++++++-------- .../WorkflowCore.Sample02.csproj | 15 +++++++-------- .../WorkflowCore.Sample03.csproj | 12 ++++++------ .../WorkflowCore.Sample04.csproj | 12 ++++++------ .../WorkflowCore.Sample05.csproj | 14 +++++++------- .../WorkflowCore.Sample06.csproj | 14 +++++++------- .../WorkflowCore.Sample07.csproj | 2 +- .../WorkflowCore.Sample08.csproj | 12 ++++++------ .../WorkflowCore.Sample09.csproj | 6 +++--- .../WorkflowCore.Sample10.csproj | 6 +++--- .../WorkflowCore.Sample11.csproj | 6 +++--- .../WorkflowCore.Sample12.csproj | 6 +++--- .../WorkflowCore.Sample13.csproj | 6 +++--- .../WorkflowCore.Sample14.csproj | 10 +++++----- .../WorkflowCore.Sample15.csproj | 6 +++--- .../WorkflowCore.Sample16.csproj | 6 +++--- .../WorkflowCore.Sample17.csproj | 4 ++-- .../WorkflowCore.TestSample01.csproj | 4 ++-- test/ScratchPad/ScratchPad.csproj | 8 ++++---- .../WorkflowCore.IntegrationTests.csproj | 6 +++--- .../WorkflowCore.Testing.csproj | 4 ++-- .../WorkflowCore.Tests.MongoDB.csproj | 6 +++--- .../WorkflowCore.Tests.PostgreSQL.csproj | 6 +++--- .../WorkflowCore.UnitTests.csproj | 6 +++--- 37 files changed, 115 insertions(+), 117 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index b1c18826e..45698aabb 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -26,8 +26,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index 35bbbce09..8d043353f 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -5,11 +5,11 @@ - + all runtime; build; native; contentfiles; analyzers - + diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.Designer.cs index e2b7d8758..b670d07ac 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.Designer.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using WorkflowCore.Persistence.PostgreSQL; using WorkflowCore.Models; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.cs index eaa3af849..c6f6d73f3 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace WorkflowCore.Persistence.PostgreSQL.Migrations { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.Designer.cs index 7c65f44a3..3ca03d35d 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.Designer.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using WorkflowCore.Persistence.PostgreSQL; using WorkflowCore.Models; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.cs index 71ee4473d..77fc5a589 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace WorkflowCore.Persistence.PostgreSQL.Migrations { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.Designer.cs index 551c7fcd4..c68ca486f 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.Designer.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using WorkflowCore.Persistence.PostgreSQL; using WorkflowCore.Models; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.Designer.cs index be3f4ca41..6e32384d0 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.Designer.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using WorkflowCore.Persistence.PostgreSQL; using WorkflowCore.Models; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.Designer.cs index a614a6f57..2bd0bd957 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.Designer.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using WorkflowCore.Persistence.PostgreSQL; using WorkflowCore.Models; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.Designer.cs index 5faad4ba3..2220458b5 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.Designer.cs @@ -1,7 +1,7 @@ // using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs index 87447fd5e..5d03522a1 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs @@ -1,7 +1,7 @@ // using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index cf9cfc5bd..21209b515 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -27,12 +27,12 @@ - - - + + + All - + diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 552e63fe5..8728ac978 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -27,11 +27,11 @@ - - + + All - + diff --git a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj index 152ccf433..9c911d78b 100644 --- a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj +++ b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj @@ -1,11 +1,10 @@  - netcoreapp1.0 + netcoreapp2.0 WorkflowCore.Sample01 Exe WorkflowCore.Sample01 - 1.0.3 $(PackageTargetFallback);dnxcore50 false false @@ -18,12 +17,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj index af734aba9..5b46f212b 100644 --- a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj +++ b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj @@ -1,11 +1,10 @@  - netcoreapp1.0 + netcoreapp2.0 WorkflowCore.Sample02 Exe WorkflowCore.Sample02 - 1.0.3 $(PackageTargetFallback);dnxcore50 false false @@ -17,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj index 5c8dcfaa8..d8f16ebe7 100644 --- a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj +++ b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj @@ -16,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index 00de2607f..a66415fc2 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -24,12 +24,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj index c4a14a0e1..31cff586e 100644 --- a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj +++ b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj @@ -1,7 +1,7 @@  - netcoreapp1.0 + netcoreapp2.0 WorkflowCore.Sample05 Exe WorkflowCore.Sample05 @@ -16,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj index 12160f810..da671ebf3 100644 --- a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj +++ b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj @@ -1,7 +1,7 @@  - netcoreapp1.0 + netcoreapp2.0 WorkflowCore.Sample06 Exe WorkflowCore.Sample06 @@ -16,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index 1fd3f91b6..989389d1e 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -26,7 +26,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj index a70da3b23..9b3e8ae83 100644 --- a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj +++ b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj @@ -21,12 +21,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj index 0a50fc7c5..8590c443e 100644 --- a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj +++ b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj index 0a50fc7c5..8590c443e 100644 --- a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj +++ b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj index d62c82859..525d8aad4 100644 --- a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj +++ b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp1.1 + netcoreapp2.0 - - + + diff --git a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj index 0c14b0512..1d4cf4a14 100644 --- a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj +++ b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj index 58ee4a9a1..dcb58fd03 100644 --- a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj +++ b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj index c7292293d..69e6c65b6 100644 --- a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj +++ b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj @@ -6,11 +6,11 @@ - - - - - + + + + + diff --git a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj index d0f8239c2..48f29e329 100644 --- a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj +++ b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj @@ -1,4 +1,4 @@ - + Exe @@ -6,8 +6,8 @@ - - + + diff --git a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj index 73f2446c6..7746f7ecb 100644 --- a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj +++ b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj @@ -1,4 +1,4 @@ - + Exe @@ -6,8 +6,8 @@ - - + + diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index e58dcab19..ee8c79856 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -1,4 +1,4 @@ - + Exe @@ -6,7 +6,7 @@ - + diff --git a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj index 117b472e3..3ce804b11 100644 --- a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj +++ b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index c2e79a161..0154c5b2a 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -17,10 +17,10 @@ - - - - + + + + diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index 957f8a481..ddde2f87a 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -20,10 +20,10 @@ - - + + - + diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index df5dc1bd9..2a402b926 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index 68d7dd9ee..efa0455be 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -22,9 +22,9 @@ - - - + + + diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index d848e9943..fe609fb15 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -23,9 +23,9 @@ - - - + + + diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index b00d948ea..a21bec422 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -19,9 +19,9 @@ - - - + + + From 4d3eca05a187c7ef51dbdbfb3b3d06a34398409f Mon Sep 17 00:00:00 2001 From: erolkskn Date: Sun, 30 Dec 2018 00:19:30 +0300 Subject: [PATCH 074/462] Add Migrations to MySQL Persistence Provider. --- ...20170126230815_InitialDatabase.Designer.cs | 222 ++++++++++++++++ .../20170126230815_InitialDatabase.cs | 213 +++++++++++++++ .../20170312161610_Events.Designer.cs | 237 +++++++++++++++++ .../Migrations/20170312161610_Events.cs | 94 +++++++ ...170507214430_ControlStructures.Designer.cs | 234 +++++++++++++++++ .../20170507214430_ControlStructures.cs | 165 ++++++++++++ .../20170519231452_PersistOutcome.Designer.cs | 238 +++++++++++++++++ .../20170519231452_PersistOutcome.cs | 22 ++ .../20170722200412_WfReference.Designer.cs | 235 +++++++++++++++++ .../Migrations/20170722200412_WfReference.cs | 23 ++ .../20171223020844_StepScope.Designer.cs | 244 ++++++++++++++++++ .../Migrations/20171223020844_StepScope.cs | 33 +++ .../MysqlPersistenceProviderModelSnapshot.cs | 238 +++++++++++++++++ .../WorkflowCore.Persistence.MySQL/README.md | 2 +- .../ServiceCollectionExtensions.cs | 4 +- .../MysqlPersistenceProviderFixture.cs | 2 +- .../Scenarios/MysqlBasicScenario.cs | 2 +- .../Scenarios/MysqlDataScenario.cs | 2 +- .../Scenarios/MysqlDynamicDataScenario.cs | 2 +- .../Scenarios/MysqlEventScenario.cs | 2 +- .../Scenarios/MysqlForeachScenario.cs | 2 +- .../Scenarios/MysqlForkScenario.cs | 2 +- .../Scenarios/MysqlIfScenario.cs | 2 +- .../Scenarios/MysqlRetrySagaScenario.cs | 2 +- .../Scenarios/MysqlSagaScenario.cs | 2 +- .../Scenarios/MysqlUserScenario.cs | 2 +- .../Scenarios/MysqlWhenScenario.cs | 2 +- .../Scenarios/MysqlWhileScenario.cs | 2 +- 28 files changed, 2214 insertions(+), 16 deletions(-) create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170126230815_InitialDatabase.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170126230815_InitialDatabase.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170312161610_Events.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170312161610_Events.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170507214430_ControlStructures.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170507214430_ControlStructures.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170519231452_PersistOutcome.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170519231452_PersistOutcome.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170722200412_WfReference.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170722200412_WfReference.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20171223020844_StepScope.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20171223020844_StepScope.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170126230815_InitialDatabase.Designer.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170126230815_InitialDatabase.Designer.cs new file mode 100644 index 000000000..3df048a55 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170126230815_InitialDatabase.Designer.cs @@ -0,0 +1,222 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + [DbContext(typeof(MysqlContext))] + [Migration("20170126230815_InitialDatabase")] + partial class InitialDatabase + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("ErrorTime"); + + b.Property("ExecutionPointerId"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("Message"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("PersistedExecutionError"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ConcurrentFork"); + + b.Property("EndTime"); + + b.Property("EventData"); + + b.Property("EventKey"); + + b.Property("EventName"); + + b.Property("EventPublished"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("PathTerminator"); + + b.Property("PersistenceData"); + + b.Property("SleepUntil"); + + b.Property("StartTime"); + + b.Property("StepId"); + + b.Property("StepName"); + + b.Property("WorkflowId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("PersistedExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("AttributeKey") + .HasMaxLength(100); + + b.Property("AttributeValue"); + + b.Property("ExecutionPointerId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("PersistedExtensionAttribute"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedPublication", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("EventData"); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("PublicationId"); + + b.Property("StepId"); + + b.Property("WorkflowId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("PublicationId") + .IsUnique(); + + b.ToTable("PersistedPublication"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("StepId"); + + b.Property("SubscriptionId") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("PersistedSubscription"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("CompleteTime"); + + b.Property("CreateTime"); + + b.Property("Data"); + + b.Property("Description") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasMaxLength(200); + + b.Property("NextExecution"); + + b.Property("Status"); + + b.Property("Version"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("PersistedWorkflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("Errors") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170126230815_InitialDatabase.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170126230815_InitialDatabase.cs new file mode 100644 index 000000000..066f861f2 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170126230815_InitialDatabase.cs @@ -0,0 +1,213 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + public partial class InitialDatabase : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "UnpublishedEvent", + columns: table => new + { + PersistenceId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + EventData = table.Column(nullable: true), + EventKey = table.Column(maxLength: 200, nullable: true), + EventName = table.Column(maxLength: 200, nullable: true), + PublicationId = table.Column(nullable: false), + StepId = table.Column(nullable: false), + WorkflowId = table.Column(maxLength: 200, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_UnpublishedEvent", x => x.PersistenceId); + }); + + migrationBuilder.CreateTable( + name: "Subscription", + columns: table => new + { + PersistenceId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + EventKey = table.Column(maxLength: 200, nullable: true), + EventName = table.Column(maxLength: 200, nullable: true), + StepId = table.Column(nullable: false), + SubscriptionId = table.Column(maxLength: 200, nullable: false), + WorkflowId = table.Column(maxLength: 200, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Subscription", x => x.PersistenceId); + }); + + migrationBuilder.CreateTable( + name: "Workflow", + columns: table => new + { + PersistenceId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CompleteTime = table.Column(nullable: true), + CreateTime = table.Column(nullable: false), + Data = table.Column(nullable: true), + Description = table.Column(maxLength: 500, nullable: true), + InstanceId = table.Column(maxLength: 200, nullable: false), + NextExecution = table.Column(nullable: true), + Status = table.Column(nullable: false), + Version = table.Column(nullable: false), + WorkflowDefinitionId = table.Column(maxLength: 200, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Workflow", x => x.PersistenceId); + }); + + migrationBuilder.CreateTable( + name: "ExecutionPointer", + columns: table => new + { + PersistenceId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Active = table.Column(nullable: false), + ConcurrentFork = table.Column(nullable: false), + EndTime = table.Column(nullable: true), + EventData = table.Column(nullable: true), + EventKey = table.Column(nullable: true), + EventName = table.Column(nullable: true), + EventPublished = table.Column(nullable: false), + Id = table.Column(maxLength: 50, nullable: true), + PathTerminator = table.Column(nullable: false), + PersistenceData = table.Column(nullable: true), + SleepUntil = table.Column(nullable: true), + StartTime = table.Column(nullable: true), + StepId = table.Column(nullable: false), + StepName = table.Column(nullable: true), + WorkflowId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExecutionPointer", x => x.PersistenceId); + table.ForeignKey( + name: "FK_ExecutionPointer_Workflow_WorkflowId", + column: x => x.WorkflowId, + principalTable: "Workflow", + principalColumn: "PersistenceId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ExecutionError", + columns: table => new + { + PersistenceId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + ErrorTime = table.Column(nullable: false), + ExecutionPointerId = table.Column(nullable: false), + Id = table.Column(maxLength: 50, nullable: true), + Message = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ExecutionError", x => x.PersistenceId); + table.ForeignKey( + name: "FK_ExecutionError_ExecutionPointer_ExecutionPointerId", + column: x => x.ExecutionPointerId, + principalTable: "ExecutionPointer", + principalColumn: "PersistenceId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ExtensionAttribute", + columns: table => new + { + PersistenceId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + AttributeKey = table.Column(maxLength: 100, nullable: true), + AttributeValue = table.Column(nullable: true), + ExecutionPointerId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExtensionAttribute", x => x.PersistenceId); + table.ForeignKey( + name: "FK_ExtensionAttribute_ExecutionPointer_ExecutionPointerId", + column: x => x.ExecutionPointerId, + principalTable: "ExecutionPointer", + principalColumn: "PersistenceId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ExecutionError_ExecutionPointerId", + table: "ExecutionError", + column: "ExecutionPointerId"); + + migrationBuilder.CreateIndex( + name: "IX_ExecutionPointer_WorkflowId", + table: "ExecutionPointer", + column: "WorkflowId"); + + migrationBuilder.CreateIndex( + name: "IX_ExtensionAttribute_ExecutionPointerId", + table: "ExtensionAttribute", + column: "ExecutionPointerId"); + + migrationBuilder.CreateIndex( + name: "IX_UnpublishedEvent_PublicationId", + table: "UnpublishedEvent", + column: "PublicationId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Subscription_EventKey", + table: "Subscription", + column: "EventKey"); + + migrationBuilder.CreateIndex( + name: "IX_Subscription_EventName", + table: "Subscription", + column: "EventName"); + + migrationBuilder.CreateIndex( + name: "IX_Subscription_SubscriptionId", + table: "Subscription", + column: "SubscriptionId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Workflow_InstanceId", + table: "Workflow", + column: "InstanceId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Workflow_NextExecution", + table: "Workflow", + column: "NextExecution"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ExecutionError"); + + migrationBuilder.DropTable( + name: "ExtensionAttribute"); + + migrationBuilder.DropTable( + name: "UnpublishedEvent"); + + migrationBuilder.DropTable( + name: "Subscription"); + + migrationBuilder.DropTable( + name: "ExecutionPointer"); + + migrationBuilder.DropTable( + name: "Workflow"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170312161610_Events.Designer.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170312161610_Events.Designer.cs new file mode 100644 index 000000000..21eb862bc --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170312161610_Events.Designer.cs @@ -0,0 +1,237 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using WorkflowCore.Persistence.MySQL; +using WorkflowCore.Models; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + [DbContext(typeof(MysqlContext))] + [Migration("20170312161610_Events")] + partial class Events + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("EventData"); + + b.Property("EventId"); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("EventTime"); + + b.Property("IsProcessed"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("PersistedEvent"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("ErrorTime"); + + b.Property("ExecutionPointerId"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("Message"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("PersistedExecutionError"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("Active"); + + b.Property("ConcurrentFork"); + + b.Property("EndTime"); + + b.Property("EventData"); + + b.Property("EventKey"); + + b.Property("EventName"); + + b.Property("EventPublished"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("PathTerminator"); + + b.Property("PersistenceData"); + + b.Property("SleepUntil"); + + b.Property("StartTime"); + + b.Property("StepId"); + + b.Property("StepName"); + + b.Property("WorkflowId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("PersistedExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("AttributeKey") + .HasMaxLength(100); + + b.Property("AttributeValue"); + + b.Property("ExecutionPointerId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("PersistedExtensionAttribute"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("StepId"); + + b.Property("SubscribeAsOf"); + + b.Property("SubscriptionId") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("PersistedSubscription"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("CompleteTime"); + + b.Property("CreateTime"); + + b.Property("Data"); + + b.Property("Description") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasMaxLength(200); + + b.Property("NextExecution"); + + b.Property("Status"); + + b.Property("Version"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("PersistedWorkflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("Errors") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170312161610_Events.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170312161610_Events.cs new file mode 100644 index 000000000..8ecba38be --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170312161610_Events.cs @@ -0,0 +1,94 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + public partial class Events : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UnpublishedEvent"); + + migrationBuilder.AddColumn( + name: "SubscribeAsOf", + table: "Subscription", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.CreateTable( + name: "Event", + columns: table => new + { + PersistenceId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + EventData = table.Column(nullable: true), + EventId = table.Column(nullable: false), + EventKey = table.Column(maxLength: 200, nullable: true), + EventName = table.Column(maxLength: 200, nullable: true), + EventTime = table.Column(nullable: false), + IsProcessed = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Event", x => x.PersistenceId); + }); + + migrationBuilder.CreateIndex( + name: "IX_Event_EventId", + table: "Event", + column: "EventId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Event_EventTime", + table: "Event", + column: "EventTime"); + + migrationBuilder.CreateIndex( + name: "IX_Event_IsProcessed", + table: "Event", + column: "IsProcessed"); + + migrationBuilder.CreateIndex( + name: "IX_Event_EventName_EventKey", + table: "Event", + columns: new[] { "EventName", "EventKey" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Event"); + + migrationBuilder.DropColumn( + name: "SubscribeAsOf", + table: "Subscription"); + + migrationBuilder.CreateTable( + name: "UnpublishedEvent", + columns: table => new + { + PersistenceId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + EventData = table.Column(nullable: true), + EventKey = table.Column(maxLength: 200, nullable: true), + EventName = table.Column(maxLength: 200, nullable: true), + PublicationId = table.Column(nullable: false), + StepId = table.Column(nullable: false), + WorkflowId = table.Column(maxLength: 200, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_UnpublishedEvent", x => x.PersistenceId); + }); + + migrationBuilder.CreateIndex( + name: "IX_UnpublishedEvent_PublicationId", + table: "UnpublishedEvent", + column: "PublicationId", + unique: true); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170507214430_ControlStructures.Designer.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170507214430_ControlStructures.Designer.cs new file mode 100644 index 000000000..161803000 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170507214430_ControlStructures.Designer.cs @@ -0,0 +1,234 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + [DbContext(typeof(MysqlContext))] + [Migration("20170507214430_ControlStructures")] + partial class ControlStructures + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("EventData"); + + b.Property("EventId"); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("EventTime"); + + b.Property("IsProcessed"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("PersistedEvent"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("ErrorTime"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100); + + b.Property("Message"); + + b.Property("WorkflowId") + .HasMaxLength(100); + + b.HasKey("PersistenceId"); + + b.ToTable("PersistedExecutionError"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("Active"); + + b.Property("Children"); + + b.Property("ContextItem"); + + b.Property("EndTime"); + + b.Property("EventData"); + + b.Property("EventKey") + .HasMaxLength(100); + + b.Property("EventName") + .HasMaxLength(100); + + b.Property("EventPublished"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("PersistenceData"); + + b.Property("PredecessorId") + .HasMaxLength(100); + + b.Property("RetryCount"); + + b.Property("SleepUntil"); + + b.Property("StartTime"); + + b.Property("StepId"); + + b.Property("StepName") + .HasMaxLength(100); + + b.Property("WorkflowId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("PersistedExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("AttributeKey") + .HasMaxLength(100); + + b.Property("AttributeValue"); + + b.Property("ExecutionPointerId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("PersistedExtensionAttribute"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("StepId"); + + b.Property("SubscribeAsOf"); + + b.Property("SubscriptionId") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("PersistedSubscription"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("CompleteTime"); + + b.Property("CreateTime"); + + b.Property("Data"); + + b.Property("Description") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasMaxLength(200); + + b.Property("NextExecution"); + + b.Property("Status"); + + b.Property("Version"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("PersistedWorkflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170507214430_ControlStructures.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170507214430_ControlStructures.cs new file mode 100644 index 000000000..afa178e63 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170507214430_ControlStructures.cs @@ -0,0 +1,165 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + public partial class ControlStructures : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ExecutionError_ExecutionPointer_ExecutionPointerId", + table: "ExecutionError"); + + migrationBuilder.DropIndex( + name: "IX_ExecutionError_ExecutionPointerId", + table: "ExecutionError"); + + migrationBuilder.DropColumn( + name: "PathTerminator", + table: "ExecutionPointer"); + + migrationBuilder.DropColumn( + name: "Id", + table: "ExecutionError"); + + migrationBuilder.RenameColumn( + name: "ConcurrentFork", + table: "ExecutionPointer", + newName: "RetryCount").Annotation("Relational:ColumnType", "int"); + + migrationBuilder.AlterColumn( + name: "StepName", + table: "ExecutionPointer", + maxLength: 100, + nullable: true, + oldClrType: typeof(string), + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "EventName", + table: "ExecutionPointer", + maxLength: 100, + nullable: true, + oldClrType: typeof(string), + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "EventKey", + table: "ExecutionPointer", + maxLength: 100, + nullable: true, + oldClrType: typeof(string), + oldNullable: true); + + migrationBuilder.AddColumn( + name: "Children", + table: "ExecutionPointer", + nullable: true); + + migrationBuilder.AddColumn( + name: "ContextItem", + table: "ExecutionPointer", + nullable: true); + + migrationBuilder.AddColumn( + name: "PredecessorId", + table: "ExecutionPointer", + maxLength: 100, + nullable: true); + + migrationBuilder.AlterColumn( + name: "ExecutionPointerId", + table: "ExecutionError", + maxLength: 100, + nullable: true, + oldClrType: typeof(long)); + + migrationBuilder.AddColumn( + name: "WorkflowId", + table: "ExecutionError", + maxLength: 100, + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Children", + table: "ExecutionPointer"); + + migrationBuilder.DropColumn( + name: "ContextItem", + table: "ExecutionPointer"); + + migrationBuilder.DropColumn( + name: "PredecessorId", + table: "ExecutionPointer"); + + migrationBuilder.DropColumn( + name: "WorkflowId", + table: "ExecutionError"); + + migrationBuilder.RenameColumn( + name: "RetryCount", + table: "ExecutionPointer", + newName: "ConcurrentFork").Annotation("Relational:ColumnType", "int"); + + migrationBuilder.AlterColumn( + name: "StepName", + table: "ExecutionPointer", + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 100, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "EventName", + table: "ExecutionPointer", + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 100, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "EventKey", + table: "ExecutionPointer", + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 100, + oldNullable: true); + + migrationBuilder.AddColumn( + name: "PathTerminator", + table: "ExecutionPointer", + nullable: false, + defaultValue: false); + + migrationBuilder.AlterColumn( + name: "ExecutionPointerId", + table: "ExecutionError", + nullable: false, + oldClrType: typeof(string), + oldMaxLength: 100, + oldNullable: true); + + migrationBuilder.AddColumn( + name: "Id", + table: "ExecutionError", + maxLength: 50, + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_ExecutionError_ExecutionPointerId", + table: "ExecutionError", + column: "ExecutionPointerId"); + + migrationBuilder.AddForeignKey( + name: "FK_ExecutionError_ExecutionPointer_ExecutionPointerId", + table: "ExecutionError", + column: "ExecutionPointerId", + principalTable: "ExecutionPointer", + principalColumn: "PersistenceId", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170519231452_PersistOutcome.Designer.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170519231452_PersistOutcome.Designer.cs new file mode 100644 index 000000000..77c52c52b --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170519231452_PersistOutcome.Designer.cs @@ -0,0 +1,238 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using WorkflowCore.Persistence.MySQL; +using WorkflowCore.Models; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + [DbContext(typeof(MysqlContext))] + [Migration("20170519231452_PersistOutcome")] + partial class PersistOutcome + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("EventData"); + + b.Property("EventId"); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("EventTime"); + + b.Property("IsProcessed"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("PersistedEvent"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("ErrorTime"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100); + + b.Property("Message"); + + b.Property("WorkflowId") + .HasMaxLength(100); + + b.HasKey("PersistenceId"); + + b.ToTable("PersistedExecutionError"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("Active"); + + b.Property("Children"); + + b.Property("ContextItem"); + + b.Property("EndTime"); + + b.Property("EventData"); + + b.Property("EventKey") + .HasMaxLength(100); + + b.Property("EventName") + .HasMaxLength(100); + + b.Property("EventPublished"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("Outcome"); + + b.Property("PersistenceData"); + + b.Property("PredecessorId") + .HasMaxLength(100); + + b.Property("RetryCount"); + + b.Property("SleepUntil"); + + b.Property("StartTime"); + + b.Property("StepId"); + + b.Property("StepName") + .HasMaxLength(100); + + b.Property("WorkflowId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("PersistedExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("AttributeKey") + .HasMaxLength(100); + + b.Property("AttributeValue"); + + b.Property("ExecutionPointerId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("PersistedExtensionAttribute"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("StepId"); + + b.Property("SubscribeAsOf"); + + b.Property("SubscriptionId") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("PersistedSubscription"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + b.Property("CompleteTime"); + + b.Property("CreateTime"); + + b.Property("Data"); + + b.Property("Description") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasMaxLength(200); + + b.Property("NextExecution"); + + b.Property("Status"); + + b.Property("Version"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("PersistedWorkflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170519231452_PersistOutcome.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170519231452_PersistOutcome.cs new file mode 100644 index 000000000..b866c55e3 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170519231452_PersistOutcome.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + public partial class PersistOutcome : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Outcome", + table: "ExecutionPointer", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Outcome", + table: "ExecutionPointer"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170722200412_WfReference.Designer.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170722200412_WfReference.Designer.cs new file mode 100644 index 000000000..4764d896b --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170722200412_WfReference.Designer.cs @@ -0,0 +1,235 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using WorkflowCore.Persistence.MySQL; +using WorkflowCore.Models; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + [DbContext(typeof(MysqlContext))] + [Migration("20170722200412_WfReference")] + partial class WfReference + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("EventData"); + + b.Property("EventId"); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("EventTime"); + + b.Property("IsProcessed"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("PersistedEvent"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("ErrorTime"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100); + + b.Property("Message"); + + b.Property("WorkflowId") + .HasMaxLength(100); + + b.HasKey("PersistenceId"); + + b.ToTable("PersistedExecutionError"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("Children"); + + b.Property("ContextItem"); + + b.Property("EndTime"); + + b.Property("EventData"); + + b.Property("EventKey") + .HasMaxLength(100); + + b.Property("EventName") + .HasMaxLength(100); + + b.Property("EventPublished"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("Outcome"); + + b.Property("PersistenceData"); + + b.Property("PredecessorId") + .HasMaxLength(100); + + b.Property("RetryCount"); + + b.Property("SleepUntil"); + + b.Property("StartTime"); + + b.Property("StepId"); + + b.Property("StepName") + .HasMaxLength(100); + + b.Property("WorkflowId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("PersistedExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("AttributeKey") + .HasMaxLength(100); + + b.Property("AttributeValue"); + + b.Property("ExecutionPointerId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("PersistedExtensionAttribute"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("StepId"); + + b.Property("SubscribeAsOf"); + + b.Property("SubscriptionId") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("PersistedSubscription"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("CompleteTime"); + + b.Property("CreateTime"); + + b.Property("Data"); + + b.Property("Description") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasMaxLength(200); + + b.Property("NextExecution"); + + b.Property("Reference") + .HasMaxLength(200); + + b.Property("Status"); + + b.Property("Version"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("PersistedWorkflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170722200412_WfReference.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170722200412_WfReference.cs new file mode 100644 index 000000000..ad738ec71 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20170722200412_WfReference.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + public partial class WfReference : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Reference", + table: "Workflow", + maxLength: 200, + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Reference", + table: "Workflow"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20171223020844_StepScope.Designer.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20171223020844_StepScope.Designer.cs new file mode 100644 index 000000000..c4eace9b7 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20171223020844_StepScope.Designer.cs @@ -0,0 +1,244 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using System; +using WorkflowCore.Models; +using WorkflowCore.Persistence.MySQL; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + [DbContext(typeof(MysqlContext))] + [Migration("20171223020844_StepScope")] + partial class StepScope + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("EventData"); + + b.Property("EventId"); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("EventTime"); + + b.Property("IsProcessed"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("ErrorTime"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100); + + b.Property("Message"); + + b.Property("WorkflowId") + .HasMaxLength(100); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("Children"); + + b.Property("ContextItem"); + + b.Property("EndTime"); + + b.Property("EventData"); + + b.Property("EventKey") + .HasMaxLength(100); + + b.Property("EventName") + .HasMaxLength(100); + + b.Property("EventPublished"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("Outcome"); + + b.Property("PersistenceData"); + + b.Property("PredecessorId") + .HasMaxLength(100); + + b.Property("RetryCount"); + + b.Property("Scope"); + + b.Property("SleepUntil"); + + b.Property("StartTime"); + + b.Property("Status"); + + b.Property("StepId"); + + b.Property("StepName") + .HasMaxLength(100); + + b.Property("WorkflowId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("AttributeKey") + .HasMaxLength(100); + + b.Property("AttributeValue"); + + b.Property("ExecutionPointerId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("StepId"); + + b.Property("SubscribeAsOf"); + + b.Property("SubscriptionId") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("CompleteTime"); + + b.Property("CreateTime"); + + b.Property("Data"); + + b.Property("Description") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasMaxLength(200); + + b.Property("NextExecution"); + + b.Property("Reference") + .HasMaxLength(200); + + b.Property("Status"); + + b.Property("Version"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20171223020844_StepScope.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20171223020844_StepScope.cs new file mode 100644 index 000000000..bec073b92 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20171223020844_StepScope.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + public partial class StepScope : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Scope", + table: "ExecutionPointer", + nullable: true); + + migrationBuilder.AddColumn( + name: "Status", + table: "ExecutionPointer", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Scope", + table: "ExecutionPointer"); + + migrationBuilder.DropColumn( + name: "Status", + table: "ExecutionPointer"); + + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs new file mode 100644 index 000000000..2e50c78d3 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs @@ -0,0 +1,238 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using System; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + [DbContext(typeof(MysqlContext))] + partial class MysqlPersistenceProviderModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("EventData"); + + b.Property("EventId"); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("EventTime"); + + b.Property("IsProcessed"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("ErrorTime"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100); + + b.Property("Message"); + + b.Property("WorkflowId") + .HasMaxLength(100); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("Children"); + + b.Property("ContextItem"); + + b.Property("EndTime"); + + b.Property("EventData"); + + b.Property("EventKey") + .HasMaxLength(100); + + b.Property("EventName") + .HasMaxLength(100); + + b.Property("EventPublished"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("Outcome"); + + b.Property("PersistenceData"); + + b.Property("PredecessorId") + .HasMaxLength(100); + + b.Property("RetryCount"); + + b.Property("Scope"); + + b.Property("SleepUntil"); + + b.Property("StartTime"); + + b.Property("Status"); + + b.Property("StepId"); + + b.Property("StepName") + .HasMaxLength(100); + + b.Property("WorkflowId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("AttributeKey") + .HasMaxLength(100); + + b.Property("AttributeValue"); + + b.Property("ExecutionPointerId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("StepId"); + + b.Property("SubscribeAsOf"); + + b.Property("SubscriptionId") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("CompleteTime"); + + b.Property("CreateTime"); + + b.Property("Data"); + + b.Property("Description") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasMaxLength(200); + + b.Property("NextExecution"); + + b.Property("Reference") + .HasMaxLength(200); + + b.Property("Status"); + + b.Property("Version"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/README.md b/src/providers/WorkflowCore.Persistence.MySQL/README.md index f24df77e1..3eca71434 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/README.md +++ b/src/providers/WorkflowCore.Persistence.MySQL/README.md @@ -15,5 +15,5 @@ PM> Install-Package WorkflowCore.Persistence.MySQL -Pre Use the .UseMySQL extension method when building your service provider. ```C# -services.AddWorkflow(x => x.UseMySQL(@"Server=127.0.0.1;Database=workflow;User=root;Password=password;", true)); +services.AddWorkflow(x => x.UseMySQL(@"Server=127.0.0.1;Database=workflow;User=root;Password=password;", true, true)); ``` diff --git a/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs index 28c22bd88..1db7980a5 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs @@ -8,9 +8,9 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { - public static WorkflowOptions UseMySQL(this WorkflowOptions options, string connectionString, bool canCreateDB, Action mysqlOptionsAction = null) + public static WorkflowOptions UseMySQL(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB, Action mysqlOptionsAction = null) { - options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new MysqlContextFactory(connectionString, mysqlOptionsAction), canCreateDB, false)); + options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new MysqlContextFactory(connectionString, mysqlOptionsAction), canCreateDB, canMigrateDB)); return options; } } diff --git a/test/WorkflowCore.Tests.MySQL/MysqlPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.MySQL/MysqlPersistenceProviderFixture.cs index ffdd71e2b..03f5501c1 100644 --- a/test/WorkflowCore.Tests.MySQL/MysqlPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.MySQL/MysqlPersistenceProviderFixture.cs @@ -16,7 +16,7 @@ public class MysqlPersistenceProviderFixture : BasePersistenceFixture public MysqlPersistenceProviderFixture(MysqlDockerSetup dockerSetup, ITestOutputHelper output) { output.WriteLine($"Connecting on {MysqlDockerSetup.ConnectionString}"); - _subject = new EntityFrameworkPersistenceProvider(new MysqlContextFactory(MysqlDockerSetup.ConnectionString), true, false); + _subject = new EntityFrameworkPersistenceProvider(new MysqlContextFactory(MysqlDockerSetup.ConnectionString), true, true); _subject.EnsureStoreExists(); } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlBasicScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlBasicScenario.cs index 8282b00f6..f2de417db 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlBasicScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlBasicScenario.cs @@ -9,7 +9,7 @@ public class MysqlBasicScenario : BasicScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDataScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDataScenario.cs index d64666373..7dbeb27c8 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDataScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDataScenario.cs @@ -9,7 +9,7 @@ public class MysqlDataScenario : DataIOScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs index 4b8065dc1..2b8dd3c9d 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs @@ -11,7 +11,7 @@ public class MysqlDynamicDataScenario : DynamicDataIOScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs index 3fbe0ca99..09838d64a 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs @@ -13,7 +13,7 @@ public class MysqlEventScenario : EventScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs index 2c4cb29a6..23111adbd 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs @@ -13,7 +13,7 @@ public class MysqlForeachScenario : ForeachScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs index d706c68d8..0d2d43421 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs @@ -13,7 +13,7 @@ public class MysqlForkScenario : ForkScenario { protected override void Configure(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs index e2fea97a6..d02f5dbe7 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs @@ -13,7 +13,7 @@ public class MysqlIfScenario : IfScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs index 41c26ed88..12dcbeb58 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs @@ -13,7 +13,7 @@ public class MysqlRetrySagaScenario : RetrySagaScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs index f540a746b..2d88ea835 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs @@ -13,7 +13,7 @@ public class MysqlSagaScenario : SagaScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs index 297930c1b..22017da65 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs @@ -13,7 +13,7 @@ public class MysqlUserScenario : UserScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs index 23ed6c79d..dae355db2 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs @@ -13,7 +13,7 @@ public class MysqlWhenScenario : WhenScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs index dc127b6e5..e6b07202e 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs @@ -13,7 +13,7 @@ public class MysqlWhileScenario : WhileScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true)); + services.AddWorkflow(x => x.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true)); } } } From 0f2f93840262b92d579da97e0bf003f08ee65503 Mon Sep 17 00:00:00 2001 From: erolkskn Date: Sun, 30 Dec 2018 00:41:55 +0300 Subject: [PATCH 075/462] Removed PackageTargetFallback from old NET Core 1.1 projects --- src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj | 1 - src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj index 9c911d78b..bd750c20f 100644 --- a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj +++ b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj @@ -5,7 +5,6 @@ WorkflowCore.Sample01 Exe WorkflowCore.Sample01 - $(PackageTargetFallback);dnxcore50 false false false diff --git a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj index 5b46f212b..3e2a90b7d 100644 --- a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj +++ b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj @@ -5,7 +5,6 @@ WorkflowCore.Sample02 Exe WorkflowCore.Sample02 - $(PackageTargetFallback);dnxcore50 false false false From 156d5bfc6a24a5b725e3b11f2a730f8964e3b207 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 30 Dec 2018 10:55:03 -0800 Subject: [PATCH 076/462] Workflow life cycle events (#215) --- ReleaseNotes/1.6.9.md | 5 + WorkflowCore.sln | 1 + .../Interface/IExecutionResultProcessor.cs | 2 +- .../Interface/ILifeCycleEventHub.cs | 14 ++ .../Interface/ILifeCycleEventPublisher.cs | 12 ++ .../Interface/IWorkflowErrorHandler.cs | 13 ++ src/WorkflowCore/Interface/IWorkflowHost.cs | 5 +- .../Models/LifeCycleEvents/LifeCycleEvent.cs | 19 +++ .../Models/LifeCycleEvents/StepCompleted.cs | 13 ++ .../Models/LifeCycleEvents/StepStarted.cs | 13 ++ .../LifeCycleEvents/WorkflowCompleted.cs | 10 ++ .../Models/LifeCycleEvents/WorkflowError.cs | 15 ++ .../Models/LifeCycleEvents/WorkflowResumed.cs | 10 ++ .../Models/LifeCycleEvents/WorkflowStarted.cs | 10 ++ .../LifeCycleEvents/WorkflowSuspended.cs | 10 ++ .../LifeCycleEvents/WorkflowTerminated.cs | 10 ++ src/WorkflowCore/Models/WorkflowOptions.cs | 8 ++ .../ServiceCollectionExtensions.cs | 9 ++ .../DefaultProviders/SingleNodeEventHub.cs | 45 ++++++ .../ErrorHandlers/CompensateHandler.cs | 98 +++++++++++++ .../Services/ErrorHandlers/RetryHandler.cs | 29 ++++ .../Services/ErrorHandlers/SuspendHandler.cs | 35 +++++ .../ErrorHandlers/TerminateHandler.cs | 35 +++++ .../Services/ExecutionResultProcessor.cs | 133 ++++++------------ .../Services/LifeCycleEventPublisher.cs | 80 +++++++++++ .../Services/WorkflowController.cs | 37 ++++- src/WorkflowCore/Services/WorkflowExecutor.cs | 35 ++++- src/WorkflowCore/Services/WorkflowHost.cs | 15 +- src/WorkflowCore/WorkflowCore.csproj | 6 +- .../ExecutionResultProcessorFixture.cs | 7 +- .../Services/WorkflowExecutorFixture.cs | 6 +- 31 files changed, 629 insertions(+), 111 deletions(-) create mode 100644 ReleaseNotes/1.6.9.md create mode 100644 src/WorkflowCore/Interface/ILifeCycleEventHub.cs create mode 100644 src/WorkflowCore/Interface/ILifeCycleEventPublisher.cs create mode 100644 src/WorkflowCore/Interface/IWorkflowErrorHandler.cs create mode 100644 src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs create mode 100644 src/WorkflowCore/Models/LifeCycleEvents/StepCompleted.cs create mode 100644 src/WorkflowCore/Models/LifeCycleEvents/StepStarted.cs create mode 100644 src/WorkflowCore/Models/LifeCycleEvents/WorkflowCompleted.cs create mode 100644 src/WorkflowCore/Models/LifeCycleEvents/WorkflowError.cs create mode 100644 src/WorkflowCore/Models/LifeCycleEvents/WorkflowResumed.cs create mode 100644 src/WorkflowCore/Models/LifeCycleEvents/WorkflowStarted.cs create mode 100644 src/WorkflowCore/Models/LifeCycleEvents/WorkflowSuspended.cs create mode 100644 src/WorkflowCore/Models/LifeCycleEvents/WorkflowTerminated.cs create mode 100644 src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs create mode 100644 src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs create mode 100644 src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs create mode 100644 src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs create mode 100644 src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs create mode 100644 src/WorkflowCore/Services/LifeCycleEventPublisher.cs diff --git a/ReleaseNotes/1.6.9.md b/ReleaseNotes/1.6.9.md new file mode 100644 index 000000000..a5a2dd3a6 --- /dev/null +++ b/ReleaseNotes/1.6.9.md @@ -0,0 +1,5 @@ +# Workflow Core 1.6.9 + +This release adds functionality to subscribe to workflow life cycle events (WorkflowStarted, WorkflowComplete, WorkflowError, WorkflowSuspended, WorkflowResumed, StepStarted, StepCompleted, etc...) +This can be achieved by either grabbing the `ILifeCycleEventHub` implementation from the IoC container and subscribing to events there, or attach an event on the workflow host class `IWorkflowHost.OnLifeCycleEvent`. +This implementation only publishes events to the local node... we will still need to implement a distributed version of the EventHub to solve the problem for multi-node clusters. \ No newline at end of file diff --git a/WorkflowCore.sln b/WorkflowCore.sln index d418c890b..89f3c8643 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -93,6 +93,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.6.0.md = ReleaseNotes\1.6.0.md ReleaseNotes\1.6.6.md = ReleaseNotes\1.6.6.md ReleaseNotes\1.6.8.md = ReleaseNotes\1.6.8.md + ReleaseNotes\1.6.9.md = ReleaseNotes\1.6.9.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" diff --git a/src/WorkflowCore/Interface/IExecutionResultProcessor.cs b/src/WorkflowCore/Interface/IExecutionResultProcessor.cs index 7d5e67c03..3ea353ada 100644 --- a/src/WorkflowCore/Interface/IExecutionResultProcessor.cs +++ b/src/WorkflowCore/Interface/IExecutionResultProcessor.cs @@ -5,7 +5,7 @@ namespace WorkflowCore.Interface { public interface IExecutionResultProcessor { - void HandleStepException(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step); + void HandleStepException(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception); void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, ExecutionResult result, WorkflowExecutorResult workflowResult); } } \ No newline at end of file diff --git a/src/WorkflowCore/Interface/ILifeCycleEventHub.cs b/src/WorkflowCore/Interface/ILifeCycleEventHub.cs new file mode 100644 index 000000000..767529841 --- /dev/null +++ b/src/WorkflowCore/Interface/ILifeCycleEventHub.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Interface +{ + public interface ILifeCycleEventHub + { + Task PublishNotification(LifeCycleEvent evt); + void Subscribe(Action action); + } +} diff --git a/src/WorkflowCore/Interface/ILifeCycleEventPublisher.cs b/src/WorkflowCore/Interface/ILifeCycleEventPublisher.cs new file mode 100644 index 000000000..273c26456 --- /dev/null +++ b/src/WorkflowCore/Interface/ILifeCycleEventPublisher.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Interface +{ + public interface ILifeCycleEventPublisher : IBackgroundTask + { + void PublishNotification(LifeCycleEvent evt); + } +} diff --git a/src/WorkflowCore/Interface/IWorkflowErrorHandler.cs b/src/WorkflowCore/Interface/IWorkflowErrorHandler.cs new file mode 100644 index 000000000..479176627 --- /dev/null +++ b/src/WorkflowCore/Interface/IWorkflowErrorHandler.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface IWorkflowErrorHandler + { + WorkflowErrorHandling Type { get; } + void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue bubbleUpQueue); + } +} diff --git a/src/WorkflowCore/Interface/IWorkflowHost.cs b/src/WorkflowCore/Interface/IWorkflowHost.cs index cc7ee151f..6bfc377db 100644 --- a/src/WorkflowCore/Interface/IWorkflowHost.cs +++ b/src/WorkflowCore/Interface/IWorkflowHost.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using WorkflowCore.Models; +using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Interface { @@ -19,6 +20,7 @@ public interface IWorkflowHost : IWorkflowController event StepErrorEventHandler OnStepError; + event LifeCycleEventHandler OnLifeCycleEvent; void ReportStepError(WorkflowInstance workflow, WorkflowStep step, Exception exception); //public dependencies to allow for extension method access @@ -32,4 +34,5 @@ public interface IWorkflowHost : IWorkflowController } public delegate void StepErrorEventHandler(WorkflowInstance workflow, WorkflowStep step, Exception exception); -} + public delegate void LifeCycleEventHandler(LifeCycleEvent evt); +} \ No newline at end of file diff --git a/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs b/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs new file mode 100644 index 000000000..6302436f4 --- /dev/null +++ b/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.LifeCycleEvents +{ + public abstract class LifeCycleEvent + { + public DateTime EventTimeUtc { get; set; } + + public string WorkflowInsanceId { get; set; } + + public string WorkflowDefinitionId { get; set; } + + public int Version { get; set; } + + public string Reference { get; set; } + } +} diff --git a/src/WorkflowCore/Models/LifeCycleEvents/StepCompleted.cs b/src/WorkflowCore/Models/LifeCycleEvents/StepCompleted.cs new file mode 100644 index 000000000..38a68697d --- /dev/null +++ b/src/WorkflowCore/Models/LifeCycleEvents/StepCompleted.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.LifeCycleEvents +{ + public class StepCompleted : LifeCycleEvent + { + public string ExecutionPointerId { get; set; } + + public int StepId { get; set; } + } +} diff --git a/src/WorkflowCore/Models/LifeCycleEvents/StepStarted.cs b/src/WorkflowCore/Models/LifeCycleEvents/StepStarted.cs new file mode 100644 index 000000000..a76c50e0f --- /dev/null +++ b/src/WorkflowCore/Models/LifeCycleEvents/StepStarted.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.LifeCycleEvents +{ + public class StepStarted : LifeCycleEvent + { + public string ExecutionPointerId { get; set; } + + public int StepId { get; set; } + } +} diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowCompleted.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowCompleted.cs new file mode 100644 index 000000000..4b3d8a2e2 --- /dev/null +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowCompleted.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.LifeCycleEvents +{ + public class WorkflowCompleted : LifeCycleEvent + { + } +} diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowError.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowError.cs new file mode 100644 index 000000000..55f273d63 --- /dev/null +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowError.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.LifeCycleEvents +{ + public class WorkflowError : LifeCycleEvent + { + public string Message { get; set; } + + public string ExecutionPointerId { get; set; } + + public int StepId { get; set; } + } +} diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowResumed.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowResumed.cs new file mode 100644 index 000000000..88f2ee603 --- /dev/null +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowResumed.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.LifeCycleEvents +{ + public class WorkflowResumed : LifeCycleEvent + { + } +} diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowStarted.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowStarted.cs new file mode 100644 index 000000000..c461ee8aa --- /dev/null +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowStarted.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.LifeCycleEvents +{ + public class WorkflowStarted : LifeCycleEvent + { + } +} diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowSuspended.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowSuspended.cs new file mode 100644 index 000000000..39d19a7a8 --- /dev/null +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowSuspended.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.LifeCycleEvents +{ + public class WorkflowSuspended : LifeCycleEvent + { + } +} diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowTerminated.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowTerminated.cs new file mode 100644 index 000000000..07ff8f75f --- /dev/null +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowTerminated.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.LifeCycleEvents +{ + public class WorkflowTerminated : LifeCycleEvent + { + } +} diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index da0536a13..f5e511dd3 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using System; +using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.Services; @@ -10,6 +11,7 @@ public class WorkflowOptions internal Func PersistanceFactory; internal Func QueueFactory; internal Func LockFactory; + internal Func EventHubFactory; internal TimeSpan PollInterval; internal TimeSpan IdleTime; internal TimeSpan ErrorRetryInterval; @@ -26,6 +28,7 @@ public WorkflowOptions(IServiceCollection services) QueueFactory = new Func(sp => new SingleNodeQueueProvider()); LockFactory = new Func(sp => new SingleNodeLockProvider()); PersistanceFactory = new Func(sp => new MemoryPersistenceProvider()); + EventHubFactory = new Func(sp => new SingleNodeEventHub(sp.GetService())); } public void UsePersistence(Func factory) @@ -43,6 +46,11 @@ public void UseQueueProvider(Func factory) QueueFactory = factory; } + public void UseEventHub(Func factory) + { + EventHubFactory = factory; + } + public void UsePollInterval(TimeSpan interval) { PollInterval = interval; diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index fada4e25d..a84e0bba0 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -11,6 +11,7 @@ using WorkflowCore.Primitives; using WorkflowCore.Services.BackgroundTasks; using WorkflowCore.Services.DefinitionStorage; +using WorkflowCore.Services.ErrorHandlers; namespace Microsoft.Extensions.DependencyInjection { @@ -26,12 +27,20 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddTransient(options.PersistanceFactory); services.AddSingleton(options.QueueFactory); services.AddSingleton(options.LockFactory); + services.AddSingleton(options.EventHubFactory); services.AddSingleton(); services.AddSingleton(options); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(sp => sp.GetService()); + + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs b/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs new file mode 100644 index 000000000..4074b9942 --- /dev/null +++ b/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Services +{ + public class SingleNodeEventHub : ILifeCycleEventHub + { + private ICollection> _subscribers = new HashSet>(); + private readonly ILogger _logger; + + public SingleNodeEventHub(ILoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + } + + public Task PublishNotification(LifeCycleEvent evt) + { + Task.Run(() => + { + foreach (var subscriber in _subscribers) + { + try + { + subscriber(evt); + } + catch (Exception ex) + { + _logger.LogWarning(default(EventId), ex, $"Error on event subscriber: {ex.Message}"); + } + } + }); + return Task.CompletedTask; + } + + public void Subscribe(Action action) + { + _subscribers.Add(action); + } + } +} diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs new file mode 100644 index 000000000..0f630ea73 --- /dev/null +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Services.ErrorHandlers +{ + public class CompensateHandler : IWorkflowErrorHandler + { + private readonly ILifeCycleEventPublisher _eventPublisher; + private readonly IExecutionPointerFactory _pointerFactory; + private readonly IDateTimeProvider _datetimeProvider; + private readonly WorkflowOptions _options; + + public WorkflowErrorHandling Type => WorkflowErrorHandling.Compensate; + + public CompensateHandler(IExecutionPointerFactory pointerFactory, ILifeCycleEventPublisher eventPublisher, IDateTimeProvider datetimeProvider, WorkflowOptions options) + { + _pointerFactory = pointerFactory; + _eventPublisher = eventPublisher; + _datetimeProvider = datetimeProvider; + _options = options; + } + + public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer exceptionPointer, WorkflowStep exceptionStep, Exception exception, Queue bubbleUpQueue) + { + var scope = new Stack(exceptionPointer.Scope); + scope.Push(exceptionPointer.Id); + + exceptionPointer.Active = false; + exceptionPointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); + exceptionPointer.Status = PointerStatus.Failed; + + while (scope.Any()) + { + var pointerId = scope.Pop(); + var scopePointer = workflow.ExecutionPointers.First(x => x.Id == pointerId); + var scopeStep = def.Steps.First(x => x.Id == scopePointer.StepId); + + var resume = true; + var revert = false; + + if (scope.Any()) + { + var parentId = scope.Peek(); + var parentPointer = workflow.ExecutionPointers.First(x => x.Id == parentId); + var parentStep = def.Steps.First(x => x.Id == parentPointer.StepId); + resume = parentStep.ResumeChildrenAfterCompensation; + revert = parentStep.RevertChildrenAfterCompensation; + } + + if ((scopeStep.ErrorBehavior ?? WorkflowErrorHandling.Compensate) != WorkflowErrorHandling.Compensate) + { + bubbleUpQueue.Enqueue(scopePointer); + continue; + } + + if (scopeStep.CompensationStepId.HasValue) + { + scopePointer.Active = false; + scopePointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); + scopePointer.Status = PointerStatus.Compensated; + + var compensationPointer = _pointerFactory.BuildCompensationPointer(def, scopePointer, exceptionPointer, scopeStep.CompensationStepId.Value); + workflow.ExecutionPointers.Add(compensationPointer); + + if (resume) + { + foreach (var outcomeTarget in scopeStep.Outcomes.Where(x => x.GetValue(workflow.Data) == null)) + workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, scopePointer, outcomeTarget)); + } + } + + if (revert) + { + var prevSiblings = workflow.ExecutionPointers + .Where(x => scopePointer.Scope.SequenceEqual(x.Scope) && x.Id != scopePointer.Id && x.Status == PointerStatus.Complete) + .OrderByDescending(x => x.EndTime) + .ToList(); + + foreach (var siblingPointer in prevSiblings) + { + var siblingStep = def.Steps.First(x => x.Id == siblingPointer.StepId); + if (siblingStep.CompensationStepId.HasValue) + { + var compensationPointer = _pointerFactory.BuildCompensationPointer(def, siblingPointer, exceptionPointer, siblingStep.CompensationStepId.Value); + workflow.ExecutionPointers.Add(compensationPointer); + siblingPointer.Status = PointerStatus.Compensated; + } + } + } + } + } + } +} diff --git a/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs new file mode 100644 index 000000000..f934922f6 --- /dev/null +++ b/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Services.ErrorHandlers +{ + public class RetryHandler : IWorkflowErrorHandler + { + private readonly IDateTimeProvider _datetimeProvider; + private readonly WorkflowOptions _options; + public WorkflowErrorHandling Type => WorkflowErrorHandling.Retry; + + public RetryHandler(IDateTimeProvider datetimeProvider, WorkflowOptions options) + { + _datetimeProvider = datetimeProvider; + _options = options; + } + + public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue bubbleUpQueue) + { + pointer.RetryCount++; + pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(step.RetryInterval ?? def.DefaultErrorRetryInterval ?? _options.ErrorRetryInterval); + step.PrimeForRetry(pointer); + } + } +} diff --git a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs new file mode 100644 index 000000000..139e5530f --- /dev/null +++ b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Services.ErrorHandlers +{ + public class SuspendHandler : IWorkflowErrorHandler + { + private readonly ILifeCycleEventPublisher _eventPublisher; + private readonly IDateTimeProvider _datetimeProvider; + public WorkflowErrorHandling Type => WorkflowErrorHandling.Suspend; + + public SuspendHandler(ILifeCycleEventPublisher eventPublisher, IDateTimeProvider datetimeProvider) + { + _eventPublisher = eventPublisher; + _datetimeProvider = datetimeProvider; + } + + public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue bubbleUpQueue) + { + workflow.Status = WorkflowStatus.Suspended; + _eventPublisher.PublishNotification(new WorkflowSuspended() + { + EventTimeUtc = _datetimeProvider.Now, + Reference = workflow.Reference, + WorkflowInsanceId = workflow.Id, + WorkflowDefinitionId = workflow.WorkflowDefinitionId, + Version = workflow.Version + }); + } + } +} diff --git a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs new file mode 100644 index 000000000..1fa192936 --- /dev/null +++ b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Services.ErrorHandlers +{ + public class TerminateHandler : IWorkflowErrorHandler + { + private readonly ILifeCycleEventPublisher _eventPublisher; + private readonly IDateTimeProvider _datetimeProvider; + public WorkflowErrorHandling Type => WorkflowErrorHandling.Terminate; + + public TerminateHandler(ILifeCycleEventPublisher eventPublisher, IDateTimeProvider datetimeProvider) + { + _eventPublisher = eventPublisher; + _datetimeProvider = datetimeProvider; + } + + public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue bubbleUpQueue) + { + workflow.Status = WorkflowStatus.Terminated; + _eventPublisher.PublishNotification(new WorkflowTerminated() + { + EventTimeUtc = _datetimeProvider.Now, + Reference = workflow.Reference, + WorkflowInsanceId = workflow.Id, + WorkflowDefinitionId = workflow.WorkflowDefinitionId, + Version = workflow.Version + }); + } + } +} diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index f27d21bd5..2f2b95746 100644 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.Models; +using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Services { @@ -12,12 +13,16 @@ public class ExecutionResultProcessor : IExecutionResultProcessor private readonly IExecutionPointerFactory _pointerFactory; private readonly IDateTimeProvider _datetimeProvider; private readonly ILogger _logger; + private readonly ILifeCycleEventPublisher _eventPublisher; + private readonly IEnumerable _errorHandlers; private readonly WorkflowOptions _options; - public ExecutionResultProcessor(IExecutionPointerFactory pointerFactory, IDateTimeProvider datetimeProvider, WorkflowOptions options, ILoggerFactory loggerFactory) + public ExecutionResultProcessor(IExecutionPointerFactory pointerFactory, IDateTimeProvider datetimeProvider, ILifeCycleEventPublisher eventPublisher, IEnumerable errorHandlers, WorkflowOptions options, ILoggerFactory loggerFactory) { _pointerFactory = pointerFactory; _datetimeProvider = datetimeProvider; + _eventPublisher = eventPublisher; + _errorHandlers = errorHandlers; _options = options; _logger = loggerFactory.CreateLogger(); } @@ -59,6 +64,17 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition { workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, pointer, outcomeTarget)); } + + _eventPublisher.PublishNotification(new StepCompleted() + { + EventTimeUtc = _datetimeProvider.Now, + Reference = workflow.Reference, + ExecutionPointerId = pointer.Id, + StepId = step.Id, + WorkflowInsanceId = workflow.Id, + WorkflowDefinitionId = workflow.WorkflowDefinitionId, + Version = workflow.Version + }); } else { @@ -72,105 +88,38 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition } } - public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step) - { - pointer.Status = PointerStatus.Failed; - var compensatingStepId = FindScopeCompensationStepId(workflow, def, pointer); - var errorOption = (step.ErrorBehavior ?? (compensatingStepId.HasValue ? WorkflowErrorHandling.Compensate : def.DefaultErrorBehavior)); - SelectErrorStrategy(errorOption, workflow, def, pointer, step); - } - - private void SelectErrorStrategy(WorkflowErrorHandling errorOption, WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step) + public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception) { - switch (errorOption) + _eventPublisher.PublishNotification(new WorkflowError() { - case WorkflowErrorHandling.Retry: - pointer.RetryCount++; - pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(step.RetryInterval ?? def.DefaultErrorRetryInterval ?? _options.ErrorRetryInterval); - step.PrimeForRetry(pointer); - break; - case WorkflowErrorHandling.Suspend: - workflow.Status = WorkflowStatus.Suspended; - break; - case WorkflowErrorHandling.Terminate: - workflow.Status = WorkflowStatus.Terminated; - break; - case WorkflowErrorHandling.Compensate: - Compensate(workflow, def, pointer); - break; - } - } - - private void Compensate(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer exceptionPointer) - { - var scope = new Stack(exceptionPointer.Scope); - scope.Push(exceptionPointer.Id); - - exceptionPointer.Active = false; - exceptionPointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); - exceptionPointer.Status = PointerStatus.Failed; - - while (scope.Any()) + EventTimeUtc = _datetimeProvider.Now, + Reference = workflow.Reference, + WorkflowInsanceId = workflow.Id, + WorkflowDefinitionId = workflow.WorkflowDefinitionId, + Version = workflow.Version, + ExecutionPointerId = pointer.Id, + StepId = step.Id, + Message = exception.Message + }); + pointer.Status = PointerStatus.Failed; + + var queue = new Queue(); + queue.Enqueue(pointer); + + while (queue.Count > 0) { - var pointerId = scope.Pop(); - var pointer = workflow.ExecutionPointers.First(x => x.Id == pointerId); - var step = def.Steps.First(x => x.Id == pointer.StepId); - - var resume = true; - var revert = false; - - if (scope.Any()) - { - var parentId = scope.Peek(); - var parentPointer = workflow.ExecutionPointers.First(x => x.Id == parentId); - var parentStep = def.Steps.First(x => x.Id == parentPointer.StepId); - resume = parentStep.ResumeChildrenAfterCompensation; - revert = parentStep.RevertChildrenAfterCompensation; - } - - if ((step.ErrorBehavior ?? WorkflowErrorHandling.Compensate) != WorkflowErrorHandling.Compensate) - { - SelectErrorStrategy(step.ErrorBehavior ?? WorkflowErrorHandling.Retry, workflow, def, pointer, step); - continue; - } + var exceptionPointer = queue.Dequeue(); + var exceptionStep = def.Steps.Find(x => x.Id == exceptionPointer.StepId); + var compensatingStepId = FindScopeCompensationStepId(workflow, def, exceptionPointer); + var errorOption = (exceptionStep.ErrorBehavior ?? (compensatingStepId.HasValue ? WorkflowErrorHandling.Compensate : def.DefaultErrorBehavior)); - if (step.CompensationStepId.HasValue) + foreach (var handler in _errorHandlers.Where(x => x.Type == errorOption)) { - pointer.Active = false; - pointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); - pointer.Status = PointerStatus.Compensated; - - var compensationPointer = _pointerFactory.BuildCompensationPointer(def, pointer, exceptionPointer, step.CompensationStepId.Value); - workflow.ExecutionPointers.Add(compensationPointer); - - if (resume) - { - foreach (var outcomeTarget in step.Outcomes.Where(x => x.GetValue(workflow.Data) == null)) - workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, pointer, outcomeTarget)); - } - } - - if (revert) - { - var prevSiblings = workflow.ExecutionPointers - .Where(x => pointer.Scope.SequenceEqual(x.Scope) && x.Id != pointer.Id && x.Status == PointerStatus.Complete) - .OrderByDescending(x => x.EndTime) - .ToList(); - - foreach (var siblingPointer in prevSiblings) - { - var siblingStep = def.Steps.First(x => x.Id == siblingPointer.StepId); - if (siblingStep.CompensationStepId.HasValue) - { - var compensationPointer = _pointerFactory.BuildCompensationPointer(def, siblingPointer, exceptionPointer, siblingStep.CompensationStepId.Value); - workflow.ExecutionPointers.Add(compensationPointer); - siblingPointer.Status = PointerStatus.Compensated; - } - } + handler.Handle(workflow, def, exceptionPointer, exceptionStep, exception, queue); } } } - + private int? FindScopeCompensationStepId(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer currentPointer) { var scope = new Stack(currentPointer.Scope); diff --git a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs new file mode 100644 index 000000000..56c13d5ec --- /dev/null +++ b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs @@ -0,0 +1,80 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Services +{ + public class LifeCycleEventPublisher : ILifeCycleEventPublisher + { + private readonly ILifeCycleEventHub _eventHub; + private readonly ILogger _logger; + private readonly ConcurrentQueue _outbox; + protected Task DispatchTask; + private CancellationTokenSource _cancellationTokenSource; + + public LifeCycleEventPublisher(ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory) + { + _eventHub = eventHub; + _outbox = new ConcurrentQueue(); + _logger = loggerFactory.CreateLogger(GetType()); + } + + public void PublishNotification(LifeCycleEvent evt) + { + _outbox.Enqueue(evt); + } + + public void Start() + { + if (DispatchTask != null) + { + throw new InvalidOperationException(); + } + + _cancellationTokenSource = new CancellationTokenSource(); + + DispatchTask = new Task(Execute); + DispatchTask.Start(); + } + + public void Stop() + { + _cancellationTokenSource.Cancel(); + DispatchTask.Wait(); + DispatchTask = null; + } + + private async void Execute() + { + var cancelToken = _cancellationTokenSource.Token; + + while (!cancelToken.IsCancellationRequested) + { + try + { + if (!SpinWait.SpinUntil(() => _outbox.Count > 0, 1000)) + { + continue; + } + + if (_outbox.TryDequeue(out LifeCycleEvent evt)) + { + await _eventHub.PublishNotification(evt); + } + } + catch (OperationCanceledException) + { + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + } + } +} diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 2bb0ebf87..da87c4cd6 100644 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -7,6 +7,7 @@ using WorkflowCore.Exceptions; using WorkflowCore.Interface; using WorkflowCore.Models; +using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Services { @@ -17,15 +18,17 @@ public class WorkflowController : IWorkflowController private readonly IWorkflowRegistry _registry; private readonly IQueueProvider _queueProvider; private readonly IExecutionPointerFactory _pointerFactory; + private readonly ILifeCycleEventHub _eventHub; private readonly ILogger _logger; - public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILoggerFactory loggerFactory) + public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory) { _persistenceStore = persistenceStore; _lockProvider = lockProvider; _registry = registry; _queueProvider = queueProvider; _pointerFactory = pointerFactory; + _eventHub = eventHub; _logger = loggerFactory.CreateLogger(); } @@ -76,6 +79,14 @@ public async Task StartWorkflow(string workflowId, int? version, string id = await _persistenceStore.CreateNewWorkflow(wf); await _queueProvider.QueueWork(id, QueueType.Workflow); + await _eventHub.PublishNotification(new WorkflowStarted() + { + EventTimeUtc = DateTime.UtcNow, + Reference = reference, + WorkflowInsanceId = id, + WorkflowDefinitionId = def.Id, + Version = def.Version + }); return id; } @@ -110,6 +121,14 @@ public async Task SuspendWorkflow(string workflowId) { wf.Status = WorkflowStatus.Suspended; await _persistenceStore.PersistWorkflow(wf); + await _eventHub.PublishNotification(new WorkflowSuspended() + { + EventTimeUtc = DateTime.UtcNow, + Reference = wf.Reference, + WorkflowInsanceId = wf.Id, + WorkflowDefinitionId = wf.WorkflowDefinitionId, + Version = wf.Version + }); return true; } @@ -137,6 +156,14 @@ public async Task ResumeWorkflow(string workflowId) wf.Status = WorkflowStatus.Runnable; await _persistenceStore.PersistWorkflow(wf); requeue = true; + await _eventHub.PublishNotification(new WorkflowResumed() + { + EventTimeUtc = DateTime.UtcNow, + Reference = wf.Reference, + WorkflowInsanceId = wf.Id, + WorkflowDefinitionId = wf.WorkflowDefinitionId, + Version = wf.Version + }); return true; } @@ -162,6 +189,14 @@ public async Task TerminateWorkflow(string workflowId) var wf = await _persistenceStore.GetWorkflowInstance(workflowId); wf.Status = WorkflowStatus.Terminated; await _persistenceStore.PersistWorkflow(wf); + await _eventHub.PublishNotification(new WorkflowTerminated() + { + EventTimeUtc = DateTime.UtcNow, + Reference = wf.Reference, + WorkflowInsanceId = wf.Id, + WorkflowDefinitionId = wf.WorkflowDefinitionId, + Version = wf.Version + }); return true; } finally diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index c0ae1278f..e1670c887 100644 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Models; +using WorkflowCore.Models.LifeCycleEvents; using WorkflowCore.Services.FluentBuilders; namespace WorkflowCore.Services @@ -19,15 +20,17 @@ public class WorkflowExecutor : IWorkflowExecutor protected readonly IDateTimeProvider _datetimeProvider; protected readonly ILogger _logger; private readonly IExecutionResultProcessor _executionResultProcessor; + private readonly ILifeCycleEventPublisher _publisher; private readonly WorkflowOptions _options; private IWorkflowHost Host => _serviceProvider.GetService(); - public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, WorkflowOptions options, ILoggerFactory loggerFactory) + public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, WorkflowOptions options, ILoggerFactory loggerFactory) { _serviceProvider = serviceProvider; _registry = registry; _datetimeProvider = datetimeProvider; + _publisher = publisher; _options = options; _logger = loggerFactory.CreateLogger(); _executionResultProcessor = executionResultProcessor; @@ -51,8 +54,7 @@ public async Task Execute(WorkflowInstance workflow) if (step != null) { try - { - pointer.Status = PointerStatus.Running; + { switch (step.InitForExecution(wfResult, def, workflow, pointer)) { case ExecutionPipelineDirective.Defer: @@ -63,6 +65,21 @@ public async Task Execute(WorkflowInstance workflow) continue; } + if (pointer.Status != PointerStatus.Running) + { + pointer.Status = PointerStatus.Running; + _publisher.PublishNotification(new StepStarted() + { + EventTimeUtc = _datetimeProvider.Now, + Reference = workflow.Reference, + ExecutionPointerId = pointer.Id, + StepId = step.Id, + WorkflowInsanceId = workflow.Id, + WorkflowDefinitionId = workflow.WorkflowDefinitionId, + Version = workflow.Version + }); + } + if (!pointer.StartTime.HasValue) { pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); @@ -127,8 +144,8 @@ public async Task Execute(WorkflowInstance workflow) ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = ex.Message }); - - _executionResultProcessor.HandleStepException(workflow, def, pointer, step); + + _executionResultProcessor.HandleStepException(workflow, def, pointer, step, ex); Host.ReportStepError(workflow, step, ex); } } @@ -258,6 +275,14 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); + _publisher.PublishNotification(new WorkflowCompleted() + { + EventTimeUtc = _datetimeProvider.Now, + Reference = workflow.Reference, + WorkflowInsanceId = workflow.Id, + WorkflowDefinitionId = workflow.WorkflowDefinitionId, + Version = workflow.Version + }); } } diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index 100bdd941..18b3da07f 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -8,6 +8,7 @@ using WorkflowCore.Models; using System.Reflection; using WorkflowCore.Exceptions; +using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Services { @@ -20,6 +21,7 @@ public class WorkflowHost : IWorkflowHost, IDisposable private readonly IWorkflowController _workflowController; public event StepErrorEventHandler OnStepError; + public event LifeCycleEventHandler OnLifeCycleEvent; // Public dependencies to allow for extension method access. public IPersistenceProvider PersistenceStore { get; private set; } @@ -29,7 +31,7 @@ public class WorkflowHost : IWorkflowHost, IDisposable public IQueueProvider QueueProvider { get; private set; } public ILogger Logger { get; private set; } - public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, WorkflowOptions options, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IEnumerable backgroundTasks, IWorkflowController workflowController) + public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, WorkflowOptions options, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IEnumerable backgroundTasks, IWorkflowController workflowController, ILifeCycleEventHub lifeCycleEventHub) { PersistenceStore = persistenceStore; QueueProvider = queueProvider; @@ -41,8 +43,9 @@ public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueP _backgroundTasks = backgroundTasks; _workflowController = workflowController; persistenceStore.EnsureStoreExists(); + lifeCycleEventHub.Subscribe(HandleLifeCycleEvent); } - + public Task StartWorkflow(string workflowId, object data = null, string reference=null) { return _workflowController.StartWorkflow(workflowId, data, reference); @@ -58,8 +61,7 @@ public Task StartWorkflow(string workflowId, TData data = null, s { return _workflowController.StartWorkflow(workflowId, null, data, reference); } - - + public Task StartWorkflow(string workflowId, int? version, TData data = null, string reference=null) where TData : class, new() { @@ -128,6 +130,11 @@ public Task TerminateWorkflow(string workflowId) return _workflowController.TerminateWorkflow(workflowId); } + public void HandleLifeCycleEvent(LifeCycleEvent evt) + { + OnLifeCycleEvent?.Invoke(evt); + } + public void ReportStepError(WorkflowInstance workflow, WorkflowStep step, Exception exception) { OnStepError?.Invoke(workflow, step, exception); diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 471edc85c..5a4babe07 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.8 - 1.6.8.0 - 1.6.8.0 + 1.6.9 + 1.6.9.0 + 1.6.9.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs index c20565c2c..18ab2427c 100644 --- a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs @@ -17,16 +17,19 @@ namespace WorkflowCore.UnitTests.Services { public class ExecutionResultProcessorFixture { - protected IExecutionResultProcessor Subject; protected IExecutionPointerFactory PointerFactory; protected IDateTimeProvider DateTimeProvider; + protected ILifeCycleEventPublisher EventHub; + protected ICollection ErrorHandlers; protected WorkflowOptions Options; public ExecutionResultProcessorFixture() { PointerFactory = A.Fake(); DateTimeProvider = A.Fake(); + EventHub = A.Fake(); + ErrorHandlers = new HashSet(); Options = new WorkflowOptions(A.Fake()); @@ -36,7 +39,7 @@ public ExecutionResultProcessorFixture() var loggerFactory = new LoggerFactory(); loggerFactory.AddConsole(LogLevel.Debug); - Subject = new ExecutionResultProcessor(PointerFactory, DateTimeProvider, Options, loggerFactory); + Subject = new ExecutionResultProcessor(PointerFactory, DateTimeProvider, EventHub, ErrorHandlers, Options, loggerFactory); } [Fact(DisplayName = "Should advance workflow")] diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 768a1fc23..7f573be1d 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -22,6 +22,7 @@ public class WorkflowExecutorFixture protected IPersistenceProvider PersistenceProvider; protected IWorkflowRegistry Registry; protected IExecutionResultProcessor ResultProcesser; + protected ILifeCycleEventPublisher EventHub; protected IServiceProvider ServiceProvider; protected IDateTimeProvider DateTimeProvider; protected WorkflowOptions Options; @@ -33,6 +34,7 @@ public WorkflowExecutorFixture() ServiceProvider = A.Fake(); Registry = A.Fake(); ResultProcesser = A.Fake(); + EventHub = A.Fake(); DateTimeProvider = A.Fake(); Options = new WorkflowOptions(A.Fake()); @@ -43,7 +45,7 @@ public WorkflowExecutorFixture() var loggerFactory = new LoggerFactory(); loggerFactory.AddConsole(LogLevel.Debug); - Subject = new WorkflowExecutor(Registry, ServiceProvider, DateTimeProvider, ResultProcesser, Options, loggerFactory); + Subject = new WorkflowExecutor(Registry, ServiceProvider, DateTimeProvider, ResultProcesser, EventHub, Options, loggerFactory); } [Fact(DisplayName = "Should execute active step")] @@ -350,7 +352,7 @@ public void should_handle_step_exception() //assert A.CallTo(() => step1Body.RunAsync(A.Ignored)).MustHaveHappened(); - A.CallTo(() => ResultProcesser.HandleStepException(instance, A.Ignored, A.Ignored, step1)).MustHaveHappened(); + A.CallTo(() => ResultProcesser.HandleStepException(instance, A.Ignored, A.Ignored, step1, A.Ignored)).MustHaveHappened(); A.CallTo(() => ResultProcesser.ProcessExecutionResult(instance, A.Ignored, A.Ignored, step1, A.Ignored, A.Ignored)).MustNotHaveHappened(); } From a15499e5a505b7d50309568e492c1a820ac5b9ab Mon Sep 17 00:00:00 2001 From: erolkskn Date: Mon, 31 Dec 2018 04:24:57 +0300 Subject: [PATCH 077/462] Update version number of packages. --- ...lowCore.Persistence.EntityFramework.csproj | 8 ++++---- .../WorkflowCore.Persistence.MySQL.csproj | 19 ++++++++++++++++++- ...WorkflowCore.Persistence.PostgreSQL.csproj | 8 ++++---- .../WorkflowCore.Persistence.SqlServer.csproj | 8 ++++---- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 45698aabb..425db8d05 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -2,7 +2,7 @@ Workflow Core EntityFramework Core Persistence Provider - 1.1.0 + 1.8.0 Daniel Gerlag netstandard2.0 WorkflowCore.Persistence.EntityFramework @@ -15,10 +15,10 @@ false false false - 1.7.0 + 1.8.0 Base package for Workflow-core peristence providers using entity framework - 1.7.0.0 - 1.7.0.0 + 1.8.0.0 + 1.8.0.0 diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index 8d043353f..b1449ac79 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -1,7 +1,24 @@ - + + Workflow Core MySQL Persistence Provider + 1.0.0 + Daniel Gerlag netstandard2.0 + WorkflowCore.Persistence.MySQL + WorkflowCore.Persistence.MySQL + workflow;.NET;Core;state machine;WorkflowCore;MySQL + https://github.com/danielgerlag/workflow-core + https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md + git + https://github.com/danielgerlag/workflow-core.git + false + false + false + Provides support to persist workflows running on Workflow Core to a MySQL database. + 1.0.0 + 1.0.0.0 + 1.0.0.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 21209b515..2a4ff7080 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -2,7 +2,7 @@ Workflow Core PostgreSQL Persistence Provider - 1.5.0 + 1.8.0 Daniel Gerlag netstandard2.0 WorkflowCore.Persistence.PostgreSQL @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 1.7.0 - 1.7.0.0 - 1.7.0.0 + 1.8.0 + 1.8.0.0 + 1.8.0.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 8728ac978..7d8e7b9bb 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -2,7 +2,7 @@ Workflow Core SQL Server Persistence Provider - 1.5.0 + 1.8.0 Daniel Gerlag netstandard2.0 WorkflowCore.Persistence.SqlServer @@ -15,10 +15,10 @@ false false false - 1.7.0 + 1.8.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 1.7.0.0 - 1.7.0.0 + 1.8.0.0 + 1.8.0.0 From 3b6ff053d2a1f99ead1e305c500f7b1a9a8ec09a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 30 Dec 2018 18:39:51 -0800 Subject: [PATCH 078/462] DynamoDB based lock manager --- .../ServiceCollectionExtensions.cs | 7 + .../Services/DynamoLockProvider.cs | 209 ++++++++++++++++++ .../WorkflowCore.Providers.AWS.csproj | 3 +- .../WorkflowCore.Sample04.csproj | 2 +- 4 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs diff --git a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs index e030c0d6c..61062e69a 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using System; +using Amazon.DynamoDBv2; using Amazon.Runtime; using Amazon.SQS; using Microsoft.Extensions.Logging; @@ -14,5 +15,11 @@ public static WorkflowOptions UseAwsSimpleQueueService(this WorkflowOptions opti options.UseQueueProvider(sp => new SQSQueueProvider(credentials, config, sp.GetService())); return options; } + + public static WorkflowOptions UseAwsDynamoLocking(this WorkflowOptions options, AWSCredentials credentials, AmazonDynamoDBConfig config, string tableName) + { + options.UseDistributedLockManager(sp => new DynamoLockProvider(credentials, config, tableName, sp.GetService())); + return options; + } } } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs new file mode 100644 index 000000000..8ca38c216 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -0,0 +1,209 @@ +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using Amazon.Runtime; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using WorkflowCore.Interface; + +namespace WorkflowCore.Providers.AWS.Services +{ + public class DynamoLockProvider : IDistributedLockProvider + { + private readonly ILogger _logger; + private readonly AmazonDynamoDBClient _client; + private readonly string _tableName; + private readonly string _nodeId; + private readonly long _ttl = 30000; + private readonly int _heartbeat = 10000; + private readonly long _jitter = 1000; + private readonly List _localLocks; + private Task _heartbeatTask; + private CancellationTokenSource _cancellationTokenSource; + + public DynamoLockProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, string tableName, ILoggerFactory logFactory) + { + _logger = logFactory.CreateLogger(); + _client = new AmazonDynamoDBClient(credentials, config); + _localLocks = new List(); + _tableName = tableName; + _nodeId = Guid.NewGuid().ToString(); + } + + public async Task AcquireLock(string Id, CancellationToken cancellationToken) + { + try + { + var req = new PutItemRequest() + { + TableName = _tableName, + Item = new Dictionary + { + { "id", new AttributeValue(Id) }, + { "lockOwner", new AttributeValue(_nodeId) }, + { "expires", new AttributeValue() + { + N = Convert.ToString(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + _ttl) + } + } + }, + ConditionExpression = "attribute_not_exists(id) OR (expires < :expired)", + ExpressionAttributeValues = new Dictionary + { + { ":expired", new AttributeValue() + { + N = Convert.ToString(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + _jitter) + } + } + } + }; + + var response = await _client.PutItemAsync(req, _cancellationTokenSource.Token); + + if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) + { + _localLocks.Add(Id); + return true; + } + } + catch (ConditionalCheckFailedException) + { + } + return false; + } + + public async Task ReleaseLock(string Id) + { + _localLocks.Remove(Id); + try + { + var req = new DeleteItemRequest() + { + TableName = _tableName, + Key = new Dictionary + { + { "id", new AttributeValue(Id) } + }, + ConditionExpression = "lockOwner = :nodeId", + ExpressionAttributeValues = new Dictionary + { + { ":nodeId", new AttributeValue(_nodeId) } + } + + }; + await _client.DeleteItemAsync(req); + } + catch (ConditionalCheckFailedException) + { + } + } + + public async Task Start() + { + await EnsureTable(); + if (_heartbeatTask != null) + { + throw new InvalidOperationException(); + } + + _cancellationTokenSource = new CancellationTokenSource(); + + _heartbeatTask = new Task(SendHeartbeat); + _heartbeatTask.Start(); + } + + public Task Stop() + { + _cancellationTokenSource.Cancel(); + _heartbeatTask.Wait(); + _heartbeatTask = null; + return Task.CompletedTask; + } + + private async void SendHeartbeat() + { + while (!_cancellationTokenSource.IsCancellationRequested) + { + try + { + await Task.Delay(_heartbeat, _cancellationTokenSource.Token); + foreach (var item in _localLocks) + { + var req = new PutItemRequest + { + TableName = _tableName, + Item = new Dictionary + { + { "id", new AttributeValue(item) }, + { "lockOwner", new AttributeValue(_nodeId) }, + { "expires", new AttributeValue() + { + N = Convert.ToString(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + _ttl) + } + } + }, + ConditionExpression = "lockOwner = :nodeId", + ExpressionAttributeValues = new Dictionary + { + { ":nodeId", new AttributeValue(_nodeId) } + } + }; + + await _client.PutItemAsync(req, _cancellationTokenSource.Token); + } + } + catch (Exception ex) + { + _logger.LogError(default(EventId), ex, ex.Message); + } + } + } + + private async Task EnsureTable() + { + try + { + var poll = await _client.DescribeTableAsync(_tableName); + } + catch (ResourceNotFoundException) + { + await CreateTable(); + } + } + + private async Task CreateTable() + { + var createRequest = new CreateTableRequest(_tableName, new List() + { + new KeySchemaElement("id", KeyType.HASH) + }) + { + AttributeDefinitions = new List() + { + new AttributeDefinition("id", ScalarAttributeType.S) + }, + BillingMode = BillingMode.PAY_PER_REQUEST + }; + + var createResponse = await _client.CreateTableAsync(createRequest); + + int i = 0; + bool created = false; + while ((i < 10) && (!created)) + { + try + { + await Task.Delay(1000); + var poll = await _client.DescribeTableAsync(_tableName, _cancellationTokenSource.Token); + created = (poll.Table.TableStatus == TableStatus.ACTIVE); + i++; + } + catch (ResourceNotFoundException) + { + } + } + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 1acb22afb..9b0283831 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -14,7 +14,8 @@ - + + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index 00de2607f..2d3fcb756 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -23,7 +23,7 @@ - + From 7594dbe4bbcee62058349937158d884e1f67d6d0 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 30 Dec 2018 19:13:37 -0800 Subject: [PATCH 079/462] config, docs --- src/providers/WorkflowCore.Providers.AWS/README.md | 9 +++++++-- .../Services/DynamoLockProvider.cs | 2 +- .../WorkflowCore.Providers.AWS.csproj | 6 ++++-- src/samples/WorkflowCore.Sample04/Program.cs | 10 +++++++++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.AWS/README.md b/src/providers/WorkflowCore.Providers.AWS/README.md index c54ba5eff..f9cef389c 100644 --- a/src/providers/WorkflowCore.Providers.AWS/README.md +++ b/src/providers/WorkflowCore.Providers.AWS/README.md @@ -1,6 +1,7 @@ # AWS providers for Workflow Core * Provides Queueing support on [Workflow Core](../../README.md) using AWS Simple Queue Service. +* Provides Distributed locking support on [Workflow Core](../../README.md) using DynamoDB. This makes it possible to have a cluster of nodes processing your workflows. @@ -14,8 +15,12 @@ PM> Install-Package WorkflowCore.Providers.AWS ## Usage -Use the .UseAwsSimpleQueueService extension method when building your service provider. +Use the `.UseAwsSimpleQueueService` and `.UseAwsDynamoLocking` extension methods when building your service provider. ```C# -services.AddWorkflow(x => x.UseAwsSimpleQueueService(awsCredentials, amazonSQSConfig)); +services.AddWorkflow(cfg => +{ + cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); + cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); +}); ``` \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs index 8ca38c216..74aaf240e 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -196,7 +196,7 @@ private async Task CreateTable() try { await Task.Delay(1000); - var poll = await _client.DescribeTableAsync(_tableName, _cancellationTokenSource.Token); + var poll = await _client.DescribeTableAsync(_tableName); created = (poll.Table.TableStatus == TableStatus.ACTIVE); i++; } diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 9b0283831..2954acd97 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -5,12 +5,14 @@ Daniel Gerlag AWS providers for Workflow Core -- Provides Queueing support on Workflow Core +- Provides Queueing support on Workflow Core +- Provides distributed locking support on Workflow Core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 1.6.0 + 1.6.9 + 1.6.9.0 diff --git a/src/samples/WorkflowCore.Sample04/Program.cs b/src/samples/WorkflowCore.Sample04/Program.cs index 9967140bb..a7c242b85 100644 --- a/src/samples/WorkflowCore.Sample04/Program.cs +++ b/src/samples/WorkflowCore.Sample04/Program.cs @@ -11,6 +11,8 @@ using WorkflowCore.Interface; using WorkflowCore.Persistence.MongoDB.Services; using WorkflowCore.Services; +using Amazon.DynamoDBv2; +using Amazon.SQS; namespace WorkflowCore.Sample04 { @@ -41,7 +43,7 @@ private static IServiceProvider ConfigureServices() //setup dependency injection IServiceCollection services = new ServiceCollection(); services.AddLogging(); - services.AddWorkflow(); + services.AddWorkflow(); //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); //services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); //services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;", true, true)); @@ -59,6 +61,12 @@ private static IServiceProvider ConfigureServices() // x.UseSqlServerLocking(@"Server=.\SQLEXPRESS;Database=WorkflowCore;Trusted_Connection=True;"); //}); + //services.AddWorkflow(cfg => + //{ + // cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); + // cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); + //}); + //services.AddWorkflow(x => x.UseRedlock(new System.Net.DnsEndPoint("127.0.0.1", 32768))); //services.AddWorkflow(x => From ce037c0abc8eacbaaf174513c04bdaafdf57f2a0 Mon Sep 17 00:00:00 2001 From: Arvin Kahbazi Date: Mon, 31 Dec 2018 08:48:52 +0330 Subject: [PATCH 080/462] Use BlockingCollection for LifeCycleEventPublisher --- .../Services/LifeCycleEventPublisher.cs | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs index 56c13d5ec..04468e0ab 100644 --- a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs +++ b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs @@ -9,24 +9,26 @@ namespace WorkflowCore.Services { - public class LifeCycleEventPublisher : ILifeCycleEventPublisher + public class LifeCycleEventPublisher : ILifeCycleEventPublisher, IDisposable { private readonly ILifeCycleEventHub _eventHub; private readonly ILogger _logger; - private readonly ConcurrentQueue _outbox; + private readonly BlockingCollection _outbox; protected Task DispatchTask; - private CancellationTokenSource _cancellationTokenSource; public LifeCycleEventPublisher(ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory) { _eventHub = eventHub; - _outbox = new ConcurrentQueue(); + _outbox = new BlockingCollection(); _logger = loggerFactory.CreateLogger(GetType()); } public void PublishNotification(LifeCycleEvent evt) { - _outbox.Enqueue(evt); + if (_outbox.IsAddingCompleted) + return; + + _outbox.Add(evt); } public void Start() @@ -36,45 +38,36 @@ public void Start() throw new InvalidOperationException(); } - _cancellationTokenSource = new CancellationTokenSource(); - DispatchTask = new Task(Execute); DispatchTask.Start(); } public void Stop() { - _cancellationTokenSource.Cancel(); + _outbox.CompleteAdding(); + DispatchTask.Wait(); DispatchTask = null; } + public void Dispose() + { + _outbox.Dispose(); + } + private async void Execute() { - var cancelToken = _cancellationTokenSource.Token; - - while (!cancelToken.IsCancellationRequested) + try { - try + foreach (var evt in _outbox.GetConsumingEnumerable()) { - if (!SpinWait.SpinUntil(() => _outbox.Count > 0, 1000)) - { - continue; - } - - if (_outbox.TryDequeue(out LifeCycleEvent evt)) - { - await _eventHub.PublishNotification(evt); - } - } - catch (OperationCanceledException) - { - } - catch (Exception ex) - { - _logger.LogError(ex.Message); + await _eventHub.PublishNotification(evt); } } + catch (Exception ex) + { + _logger.LogError(default(EventId), ex, ex.Message); + } } } -} +} \ No newline at end of file From 18bfbe54aaf7c283940c4e99513874baf791bf24 Mon Sep 17 00:00:00 2001 From: Kevin Preller Date: Mon, 31 Dec 2018 16:20:22 +0100 Subject: [PATCH 081/462] Fixed typo WorkflowInsanceId => WorkflowInstanceId --- src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs | 2 +- src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs | 2 +- .../Services/ErrorHandlers/TerminateHandler.cs | 2 +- src/WorkflowCore/Services/ExecutionResultProcessor.cs | 4 ++-- src/WorkflowCore/Services/WorkflowController.cs | 8 ++++---- src/WorkflowCore/Services/WorkflowExecutor.cs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) mode change 100644 => 100755 src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs mode change 100644 => 100755 src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs mode change 100644 => 100755 src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs mode change 100644 => 100755 src/WorkflowCore/Services/ExecutionResultProcessor.cs mode change 100644 => 100755 src/WorkflowCore/Services/WorkflowController.cs mode change 100644 => 100755 src/WorkflowCore/Services/WorkflowExecutor.cs diff --git a/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs b/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs old mode 100644 new mode 100755 index 6302436f4..afab488b0 --- a/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs +++ b/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs @@ -8,7 +8,7 @@ public abstract class LifeCycleEvent { public DateTime EventTimeUtc { get; set; } - public string WorkflowInsanceId { get; set; } + public string WorkflowInstanceId { get; set; } public string WorkflowDefinitionId { get; set; } diff --git a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs old mode 100644 new mode 100755 index 139e5530f..b0aec84fd --- a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs @@ -26,7 +26,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP { EventTimeUtc = _datetimeProvider.Now, Reference = workflow.Reference, - WorkflowInsanceId = workflow.Id, + WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version }); diff --git a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs old mode 100644 new mode 100755 index 1fa192936..3745fe483 --- a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs @@ -26,7 +26,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP { EventTimeUtc = _datetimeProvider.Now, Reference = workflow.Reference, - WorkflowInsanceId = workflow.Id, + WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version }); diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs old mode 100644 new mode 100755 index 2f2b95746..13b30afb8 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -71,7 +71,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition Reference = workflow.Reference, ExecutionPointerId = pointer.Id, StepId = step.Id, - WorkflowInsanceId = workflow.Id, + WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version }); @@ -94,7 +94,7 @@ public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition de { EventTimeUtc = _datetimeProvider.Now, Reference = workflow.Reference, - WorkflowInsanceId = workflow.Id, + WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version, ExecutionPointerId = pointer.Id, diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs old mode 100644 new mode 100755 index da87c4cd6..5702c657e --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -83,7 +83,7 @@ await _eventHub.PublishNotification(new WorkflowStarted() { EventTimeUtc = DateTime.UtcNow, Reference = reference, - WorkflowInsanceId = id, + WorkflowInstanceId = id, WorkflowDefinitionId = def.Id, Version = def.Version }); @@ -125,7 +125,7 @@ await _eventHub.PublishNotification(new WorkflowSuspended() { EventTimeUtc = DateTime.UtcNow, Reference = wf.Reference, - WorkflowInsanceId = wf.Id, + WorkflowInstanceId = wf.Id, WorkflowDefinitionId = wf.WorkflowDefinitionId, Version = wf.Version }); @@ -160,7 +160,7 @@ await _eventHub.PublishNotification(new WorkflowResumed() { EventTimeUtc = DateTime.UtcNow, Reference = wf.Reference, - WorkflowInsanceId = wf.Id, + WorkflowInstanceId = wf.Id, WorkflowDefinitionId = wf.WorkflowDefinitionId, Version = wf.Version }); @@ -193,7 +193,7 @@ await _eventHub.PublishNotification(new WorkflowTerminated() { EventTimeUtc = DateTime.UtcNow, Reference = wf.Reference, - WorkflowInsanceId = wf.Id, + WorkflowInstanceId = wf.Id, WorkflowDefinitionId = wf.WorkflowDefinitionId, Version = wf.Version }); diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs old mode 100644 new mode 100755 index e1670c887..0dd00f45b --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -74,7 +74,7 @@ public async Task Execute(WorkflowInstance workflow) Reference = workflow.Reference, ExecutionPointerId = pointer.Id, StepId = step.Id, - WorkflowInsanceId = workflow.Id, + WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version }); @@ -279,7 +279,7 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) { EventTimeUtc = _datetimeProvider.Now, Reference = workflow.Reference, - WorkflowInsanceId = workflow.Id, + WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version }); From 3717923af9dc54352e4d7931c3095ec37def91bc Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 31 Dec 2018 10:35:09 -0800 Subject: [PATCH 082/462] Update WorkflowCore.csproj --- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 5a4babe07..fd442dedf 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.9 - 1.6.9.0 - 1.6.9.0 + 1.6.10 + 1.6.10.0 + 1.6.10.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From 1f21b41eb6e893f5a594f525144523d55903f3a9 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 31 Dec 2018 10:43:43 -0800 Subject: [PATCH 083/462] move try/catch down --- .../Services/LifeCycleEventPublisher.cs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs index 04468e0ab..25334bcd2 100644 --- a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs +++ b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs @@ -14,7 +14,7 @@ public class LifeCycleEventPublisher : ILifeCycleEventPublisher, IDisposable private readonly ILifeCycleEventHub _eventHub; private readonly ILogger _logger; private readonly BlockingCollection _outbox; - protected Task DispatchTask; + private Task _dispatchTask; public LifeCycleEventPublisher(ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory) { @@ -33,21 +33,20 @@ public void PublishNotification(LifeCycleEvent evt) public void Start() { - if (DispatchTask != null) + if (_dispatchTask != null) { throw new InvalidOperationException(); } - DispatchTask = new Task(Execute); - DispatchTask.Start(); + _dispatchTask = new Task(Execute); + _dispatchTask.Start(); } public void Stop() { _outbox.CompleteAdding(); - - DispatchTask.Wait(); - DispatchTask = null; + _dispatchTask.Wait(); + _dispatchTask = null; } public void Dispose() @@ -57,16 +56,16 @@ public void Dispose() private async void Execute() { - try + foreach (var evt in _outbox.GetConsumingEnumerable()) { - foreach (var evt in _outbox.GetConsumingEnumerable()) + try { await _eventHub.PublishNotification(evt); } - } - catch (Exception ex) - { - _logger.LogError(default(EventId), ex, ex.Message); + catch (Exception ex) + { + _logger.LogError(default(EventId), ex, ex.Message); + } } } } From 0ac2eedbb05eb6b4a5d47de17e8542a7250a457f Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 6 Jan 2019 07:51:05 -0800 Subject: [PATCH 084/462] Amazon DynamoDB persistence provider (#227) --- README.md | 1 + .../DynamoDbDockerSetup.cs | 55 +++ .../DynamoPersistenceProviderFixture.cs | 40 +++ .../Scenarios/DynamoBasicScenario.cs | 22 ++ .../Scenarios/DynamoCompensationScenario.cs | 22 ++ .../Scenarios/DynamoDataScenario.cs | 22 ++ .../Scenarios/DynamoDynamicDataScenario.cs | 22 ++ .../Scenarios/DynamoEventScenario.cs | 22 ++ .../Scenarios/DynamoForeachScenario.cs | 22 ++ .../Scenarios/DynamoIfScenario.cs | 22 ++ .../Scenarios/DynamoRetrySagaScenario.cs | 22 ++ .../Scenarios/DynamoSagaScenario.cs | 22 ++ .../Scenarios/DynamoWhileScenario.cs | 22 ++ .../WorkflowCore.Tests.DynamoDB.csproj | 23 ++ WorkflowCore.sln | 7 + WorkflowCore.sln.DotSettings | 1 + src/WorkflowCore/Services/WorkflowHost.cs | 1 - src/WorkflowCore/WorkflowCore.csproj | 6 +- .../ModelExtensions.cs | 143 ++++++++ .../WorkflowCore.Providers.AWS/README.md | 18 +- .../ServiceCollectionExtensions.cs | 7 + .../Services/DynamoDbProvisioner.cs | 276 +++++++++++++++ .../Services/DynamoLockProvider.cs | 10 +- .../Services/DynamoPersistenceProvider.cs | 333 ++++++++++++++++++ .../Services/IDynamoDbProvisioner.cs | 9 + .../WorkflowCore.Providers.AWS.csproj | 5 +- src/samples/WorkflowCore.Sample04/Program.cs | 7 +- test/Docker.Testify/Docker.Testify.csproj | 4 +- test/Docker.Testify/DockerSetup.cs | 11 +- test/ScratchPad/Program.cs | 42 ++- test/ScratchPad/ScratchPad.csproj | 2 + test/WorkflowCore.Tests.MySQL/DockerSetup.cs | 2 + .../DockerSetup.cs | 2 +- 33 files changed, 1186 insertions(+), 39 deletions(-) create mode 100644 WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs create mode 100644 WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs create mode 100644 WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs create mode 100644 WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs create mode 100644 WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs create mode 100644 WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs create mode 100644 WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs create mode 100644 WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs create mode 100644 WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs create mode 100644 WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs create mode 100644 WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs create mode 100644 WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs create mode 100644 WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj create mode 100644 src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs create mode 100644 src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs create mode 100644 src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs create mode 100644 src/providers/WorkflowCore.Providers.AWS/Services/IDynamoDbProvisioner.cs diff --git a/README.md b/README.md index 259736b5f..f78c34012 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ There are several persistence providers available as separate Nuget packages. * MemoryPersistenceProvider *(Default provider, for demo and testing purposes)* * [MongoDB](src/providers/WorkflowCore.Persistence.MongoDB) +* [Amazon DynamoDB](src/providers/WorkflowCore.Providers.AWS) * [SQL Server](src/providers/WorkflowCore.Persistence.SqlServer) * [PostgreSQL](src/providers/WorkflowCore.Persistence.PostgreSQL) * [Sqlite](src/providers/WorkflowCore.Persistence.Sqlite) diff --git a/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs b/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs new file mode 100644 index 000000000..a166ec804 --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs @@ -0,0 +1,55 @@ +using Docker.DotNet; +using Docker.DotNet.Models; +using System; +using System.Collections.Generic; +using System.Net; +using System.Runtime.InteropServices; +using System.Text; +using Docker.Testify; +using Xunit; +using Amazon.DynamoDBv2; +using Amazon.Runtime; + +namespace WorkflowCore.Tests.DynamoDB +{ + public class DynamoDbDockerSetup : DockerSetup + { + public static string ConnectionString { get; set; } + + public static AWSCredentials Credentials => new EnvironmentVariablesAWSCredentials(); + + public override string ImageName => @"amazon/dynamodb-local"; + public override int InternalPort => 8000; + + public override void PublishConnectionInfo() + { + ConnectionString = $"http://localhost:{ExternalPort}"; + } + + public override bool TestReady() + { + try + { + AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig + { + ServiceURL = $"http://localhost:{ExternalPort}" + }; + AmazonDynamoDBClient client = new AmazonDynamoDBClient(clientConfig); + var resp = client.ListTablesAsync().Result; + + return resp.HttpStatusCode == HttpStatusCode.OK; + } + catch + { + return false; + } + + } + } + + [CollectionDefinition("DynamoDb collection")] + public class DynamoDbCollection : ICollectionFixture + { + } + +} diff --git a/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs b/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs new file mode 100644 index 000000000..aa0663936 --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; +using WorkflowCore.Providers.AWS.Services; +using WorkflowCore.UnitTests; +using Xunit; + +namespace WorkflowCore.Tests.DynamoDB +{ + [Collection("DynamoDb collection")] + public class DynamoPersistenceProviderFixture : BasePersistenceFixture + { + DynamoDbDockerSetup _dockerSetup; + private IPersistenceProvider _subject; + + public DynamoPersistenceProviderFixture(DynamoDbDockerSetup dockerSetup) + { + _dockerSetup = dockerSetup; + } + + protected override IPersistenceProvider Subject + { + get + { + if (_subject == null) + { + var cfg = new AmazonDynamoDBConfig { ServiceURL = DynamoDbDockerSetup.ConnectionString }; + var provisioner = new DynamoDbProvisioner(DynamoDbDockerSetup.Credentials, cfg, "unittests", new LoggerFactory()); + var client = new DynamoPersistenceProvider(DynamoDbDockerSetup.Credentials, cfg, provisioner, "unittests", new LoggerFactory()); + client.EnsureStoreExists(); + _subject = client; + } + return _subject; + } + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs new file mode 100644 index 000000000..dad0dd2ae --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.DynamoDB; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("DynamoDb collection")] + public class DynamoBasicScenario : BasicScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + var cfg = new AmazonDynamoDBConfig {ServiceURL = DynamoDbDockerSetup.ConnectionString}; + services.AddWorkflow(x => x.UseAwsDynamoPersistence(DynamoDbDockerSetup.Credentials, cfg, "tests-")); + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs new file mode 100644 index 000000000..0c247d648 --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.DynamoDB; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("DynamoDb collection")] + public class DynamoCompensationScenario : CompensationScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + var cfg = new AmazonDynamoDBConfig {ServiceURL = DynamoDbDockerSetup.ConnectionString}; + services.AddWorkflow(x => x.UseAwsDynamoPersistence(DynamoDbDockerSetup.Credentials, cfg, "tests-")); + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs new file mode 100644 index 000000000..b6fffaa51 --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.DynamoDB; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("DynamoDb collection")] + public class DynamoDataScenario : DataIOScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + var cfg = new AmazonDynamoDBConfig {ServiceURL = DynamoDbDockerSetup.ConnectionString}; + services.AddWorkflow(x => x.UseAwsDynamoPersistence(DynamoDbDockerSetup.Credentials, cfg, "tests-")); + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs new file mode 100644 index 000000000..f84122ae0 --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.DynamoDB; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("DynamoDb collection")] + public class DynamoDynamicDataScenario : DynamicDataIOScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + var cfg = new AmazonDynamoDBConfig {ServiceURL = DynamoDbDockerSetup.ConnectionString}; + services.AddWorkflow(x => x.UseAwsDynamoPersistence(DynamoDbDockerSetup.Credentials, cfg, "tests-")); + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs new file mode 100644 index 000000000..1999c35ad --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.DynamoDB; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("DynamoDb collection")] + public class DynamoEventScenario : EventScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + var cfg = new AmazonDynamoDBConfig {ServiceURL = DynamoDbDockerSetup.ConnectionString}; + services.AddWorkflow(x => x.UseAwsDynamoPersistence(DynamoDbDockerSetup.Credentials, cfg, "tests-")); + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs new file mode 100644 index 000000000..5c7443a32 --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.DynamoDB; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("DynamoDb collection")] + public class DynamoForeachScenario : ForeachScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + var cfg = new AmazonDynamoDBConfig {ServiceURL = DynamoDbDockerSetup.ConnectionString}; + services.AddWorkflow(x => x.UseAwsDynamoPersistence(DynamoDbDockerSetup.Credentials, cfg, "tests-")); + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs new file mode 100644 index 000000000..7a2dc1d14 --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.DynamoDB; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("DynamoDb collection")] + public class DynamoIfScenario : IfScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + var cfg = new AmazonDynamoDBConfig {ServiceURL = DynamoDbDockerSetup.ConnectionString}; + services.AddWorkflow(x => x.UseAwsDynamoPersistence(DynamoDbDockerSetup.Credentials, cfg, "tests-")); + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs new file mode 100644 index 000000000..f46266b99 --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.DynamoDB; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("DynamoDb collection")] + public class DynamoRetrySagaScenario : RetrySagaScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + var cfg = new AmazonDynamoDBConfig {ServiceURL = DynamoDbDockerSetup.ConnectionString}; + services.AddWorkflow(x => x.UseAwsDynamoPersistence(DynamoDbDockerSetup.Credentials, cfg, "tests-")); + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs new file mode 100644 index 000000000..47f5063b9 --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.DynamoDB; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("DynamoDb collection")] + public class DynamoSagaScenario : SagaScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + var cfg = new AmazonDynamoDBConfig {ServiceURL = DynamoDbDockerSetup.ConnectionString}; + services.AddWorkflow(x => x.UseAwsDynamoPersistence(DynamoDbDockerSetup.Credentials, cfg, "tests-")); + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs new file mode 100644 index 000000000..ae11abbbd --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using WorkflowCore.Tests.DynamoDB; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("DynamoDb collection")] + public class DynamoWhileScenario : WhileScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + var cfg = new AmazonDynamoDBConfig {ServiceURL = DynamoDbDockerSetup.ConnectionString}; + services.AddWorkflow(x => x.UseAwsDynamoPersistence(DynamoDbDockerSetup.Credentials, cfg, "tests-")); + } + } +} diff --git a/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj new file mode 100644 index 000000000..0a733560f --- /dev/null +++ b/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp2.1 + + false + + + + + + + + + + + + + + + + + diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 89f3c8643..30a4b7930 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -120,6 +120,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Persistence.My EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.MySQL", "test\WorkflowCore.Tests.MySQL\WorkflowCore.Tests.MySQL.csproj", "{DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.DynamoDB", "WorkflowCore.Tests.DynamoDB\WorkflowCore.Tests.DynamoDB.csproj", "{0FF3F27E-E909-4ABA-BF82-39D1BA133EA7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -302,6 +304,10 @@ Global {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}.Release|Any CPU.Build.0 = Release|Any CPU + {0FF3F27E-E909-4ABA-BF82-39D1BA133EA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0FF3F27E-E909-4ABA-BF82-39D1BA133EA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FF3F27E-E909-4ABA-BF82-39D1BA133EA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0FF3F27E-E909-4ABA-BF82-39D1BA133EA7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -354,6 +360,7 @@ Global {5E82A137-0954-46A1-8C46-13C00F0E4842} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {453E260D-DBDC-4DDC-BC9C-CA500CED7897} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} + {0FF3F27E-E909-4ABA-BF82-39D1BA133EA7} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/WorkflowCore.sln.DotSettings b/WorkflowCore.sln.DotSettings index f7baff92b..f25600f78 100644 --- a/WorkflowCore.sln.DotSettings +++ b/WorkflowCore.sln.DotSettings @@ -12,6 +12,7 @@ True True True + True True True True diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index 18b3da07f..6a4c91d22 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -42,7 +42,6 @@ public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueP LockProvider = lockProvider; _backgroundTasks = backgroundTasks; _workflowController = workflowController; - persistenceStore.EnsureStoreExists(); lifeCycleEventHub.Subscribe(HandleLifeCycleEvent); } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index fd442dedf..79ac162f4 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.10 - 1.6.10.0 - 1.6.10.0 + 1.6.11 + 1.6.11.0 + 1.6.11.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs new file mode 100644 index 000000000..5d38f094f --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs @@ -0,0 +1,143 @@ +using Amazon.DynamoDBv2.Model; +using Amazon.Util; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Bson; +using WorkflowCore.Models; + +namespace WorkflowCore.Providers.AWS +{ + internal static class ModelExtensions + { + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + + public static Dictionary ToDynamoMap(this WorkflowInstance source) + { + var result = new Dictionary(); + + result["id"] = new AttributeValue(source.Id); + result["workflow_definition_id"] = new AttributeValue(source.WorkflowDefinitionId); + result["version"] = new AttributeValue(source.Version.ToString()); + result["next_execution"] = new AttributeValue() { N = (source.NextExecution ?? 0).ToString() }; + result["create_time"] = new AttributeValue() { N = source.CreateTime.Ticks.ToString() }; + result["data"] = new AttributeValue(JsonConvert.SerializeObject(source.Data, SerializerSettings)); + result["workflow_status"] = new AttributeValue() { N = Convert.ToInt32(source.Status).ToString() }; + + if (!string.IsNullOrEmpty(source.Description)) + result["description"] = new AttributeValue(source.Description); + + if (!string.IsNullOrEmpty(source.Reference)) + result["reference"] = new AttributeValue(source.Reference); + + if (source.CompleteTime.HasValue) + result["complete_time"] = new AttributeValue() { N = source.CompleteTime.Value.Ticks.ToString() }; + + var pointers = new List(); + foreach (var pointer in source.ExecutionPointers) + { + pointers.Add(new AttributeValue(JsonConvert.SerializeObject(pointer, SerializerSettings))); + } + + result["pointers"] = new AttributeValue() { L = pointers }; + + if (source.Status == WorkflowStatus.Runnable) + result["runnable"] = new AttributeValue() { N = 1.ToString() }; + + return result; + } + + public static WorkflowInstance ToWorkflowInstance(this Dictionary source) + { + var result = new WorkflowInstance() + { + Id = source["id"].S, + WorkflowDefinitionId = source["workflow_definition_id"].S, + Version = Convert.ToInt32(source["version"].S), + Status = (WorkflowStatus)Convert.ToInt32(source["workflow_status"].N), + NextExecution = Convert.ToInt64(source["next_execution"].N), + CreateTime = new DateTime(Convert.ToInt64(source["create_time"].N)), + Data = JsonConvert.DeserializeObject(source["data"].S, SerializerSettings) + }; + + if (source.ContainsKey("description")) + result.Description = source["description"].S; + + if (source.ContainsKey("reference")) + result.Reference = source["reference"].S; + + if (source.ContainsKey("complete_time")) + result.CompleteTime = new DateTime(Int64.Parse(source["complete_time"].N)); + + foreach (var pointer in source["pointers"].L) + { + var ep = JsonConvert.DeserializeObject(pointer.S, SerializerSettings); + result.ExecutionPointers.Add(ep); + } + + return result; + } + + public static Dictionary ToDynamoMap(this EventSubscription source) + { + return new Dictionary + { + ["id"] = new AttributeValue(source.Id), + ["event_name"] = new AttributeValue(source.EventName), + ["event_key"] = new AttributeValue(source.EventKey), + ["workflow_id"] = new AttributeValue(source.WorkflowId), + ["step_id"] = new AttributeValue(source.StepId.ToString()), + ["subscribe_as_of"] = new AttributeValue() { N = source.SubscribeAsOf.Ticks.ToString() }, + ["event_slug"] = new AttributeValue($"{source.EventName}:{source.EventKey}") + }; + } + + public static EventSubscription ToEventSubscription(this Dictionary source) + { + return new EventSubscription() + { + Id = source["id"].S, + EventName = source["event_name"].S, + EventKey = source["event_key"].S, + WorkflowId = source["workflow_id"].S, + StepId = Convert.ToInt32(source["step_id"].S), + SubscribeAsOf = new DateTime(Convert.ToInt64(source["subscribe_as_of"].N)) + }; + } + + public static Dictionary ToDynamoMap(this Event source) + { + var result = new Dictionary + { + ["id"] = new AttributeValue(source.Id), + ["event_name"] = new AttributeValue(source.EventName), + ["event_key"] = new AttributeValue(source.EventKey), + ["event_data"] = new AttributeValue(JsonConvert.SerializeObject(source.EventData, SerializerSettings)), + ["event_time"] = new AttributeValue() { N = source.EventTime.Ticks.ToString() }, + ["event_slug"] = new AttributeValue($"{source.EventName}:{source.EventKey}") + }; + + if (!source.IsProcessed) + result["not_processed"] = new AttributeValue() { N = 1.ToString() }; + + return result; + } + + public static Event ToEvent(this Dictionary source) + { + var result = new Event() + { + Id = source["id"].S, + EventName = source["event_name"].S, + EventKey = source["event_key"].S, + EventData = JsonConvert.DeserializeObject(source["event_data"].S, SerializerSettings), + EventTime = new DateTime(Convert.ToInt64(source["event_time"].N)), + IsProcessed = (!source.ContainsKey("not_processed")) + }; + + return result; + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.AWS/README.md b/src/providers/WorkflowCore.Providers.AWS/README.md index f9cef389c..a729b3828 100644 --- a/src/providers/WorkflowCore.Providers.AWS/README.md +++ b/src/providers/WorkflowCore.Providers.AWS/README.md @@ -1,7 +1,8 @@ # AWS providers for Workflow Core -* Provides Queueing support on [Workflow Core](../../README.md) using AWS Simple Queue Service. -* Provides Distributed locking support on [Workflow Core](../../README.md) using DynamoDB. +* Provides persistence for [Workflow Core](../../README.md) using DynamoDB. +* Provides Queueing support on [Workflow Core](../../README.md) using AWS Simple Queue Service. +* Provides Distributed locking support on [Workflow Core](../../README.md) using DynamoDB. This makes it possible to have a cluster of nodes processing your workflows. @@ -15,12 +16,19 @@ PM> Install-Package WorkflowCore.Providers.AWS ## Usage -Use the `.UseAwsSimpleQueueService` and `.UseAwsDynamoLocking` extension methods when building your service provider. +Use the `IServiceCollection` extension methods when building your service provider +* .UseAwsDynamoPersistence +* .UseAwsSimpleQueueService +* .UseAwsDynamoLocking ```C# services.AddWorkflow(cfg => { - cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); + cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "table-prefix"); cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); + cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); }); -``` \ No newline at end of file +``` + +If any AWS resources do not exists, they will be automatcially created. By default, all DynamoDB tables and indexes will be provisioned with a throughput of 1, you can modify these values from the AWS console. +You may also specify a prefix for the dynamo table names. \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs index 61062e69a..84ed7fe8b 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs @@ -21,5 +21,12 @@ public static WorkflowOptions UseAwsDynamoLocking(this WorkflowOptions options, options.UseDistributedLockManager(sp => new DynamoLockProvider(credentials, config, tableName, sp.GetService())); return options; } + + public static WorkflowOptions UseAwsDynamoPersistence(this WorkflowOptions options, AWSCredentials credentials, AmazonDynamoDBConfig config, string tablePrefix) + { + options.Services.AddTransient(sp => new DynamoDbProvisioner(credentials, config, tablePrefix, sp.GetService())); + options.UsePersistence(sp => new DynamoPersistenceProvider(credentials, config, sp.GetService(), tablePrefix, sp.GetService())); + return options; + } } } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs new file mode 100644 index 000000000..8dd2fcfa9 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs @@ -0,0 +1,276 @@ +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using Amazon.Runtime; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using WorkflowCore.Interface; + +namespace WorkflowCore.Providers.AWS.Services +{ + public class DynamoDbProvisioner : IDynamoDbProvisioner + { + private readonly ILogger _logger; + private readonly AmazonDynamoDBClient _client; + private readonly string _tablePrefix; + + public DynamoDbProvisioner(AWSCredentials credentials, AmazonDynamoDBConfig config, string tablePrefix, ILoggerFactory logFactory) + { + _logger = logFactory.CreateLogger(); + _client = new AmazonDynamoDBClient(credentials, config); + _tablePrefix = tablePrefix; + } + + public Task ProvisionTables() + { + return Task.WhenAll( + EnsureTable($"{_tablePrefix}-{DynamoPersistenceProvider.WORKFLOW_TABLE}", CreateWorkflowTable), + EnsureTable($"{_tablePrefix}-{DynamoPersistenceProvider.SUBCRIPTION_TABLE}", CreateSubscriptionTable), + EnsureTable($"{_tablePrefix}-{DynamoPersistenceProvider.EVENT_TABLE}", CreateEventTable)); + } + + private async Task CreateWorkflowTable() + { + var runnableIndex = new GlobalSecondaryIndex() + { + IndexName = "ix_runnable", + KeySchema = new List() + { + { + new KeySchemaElement + { + AttributeName= "runnable", + KeyType = "HASH" //Partition key + } + }, + { + new KeySchemaElement + { + AttributeName = "next_execution", + KeyType = "RANGE" //Sort key + } + } + }, + Projection = new Projection() + { + ProjectionType = ProjectionType.KEYS_ONLY + }, + ProvisionedThroughput = new ProvisionedThroughput() + { + ReadCapacityUnits = 1, + WriteCapacityUnits = 1 + } + }; + + var createRequest = new CreateTableRequest($"{_tablePrefix}-{DynamoPersistenceProvider.WORKFLOW_TABLE}", new List() + { + new KeySchemaElement("id", KeyType.HASH) + }) + { + AttributeDefinitions = new List() + { + new AttributeDefinition("id", ScalarAttributeType.S), + new AttributeDefinition("runnable", ScalarAttributeType.N), + new AttributeDefinition("next_execution", ScalarAttributeType.N), + }, + GlobalSecondaryIndexes = new List() + { + runnableIndex + }, + ProvisionedThroughput = new ProvisionedThroughput() + { + ReadCapacityUnits = 1, + WriteCapacityUnits = 1 + } + }; + + await CreateTable(createRequest); + } + + private async Task CreateSubscriptionTable() + { + var slugIndex = new GlobalSecondaryIndex() + { + IndexName = "ix_slug", + KeySchema = new List() + { + { + new KeySchemaElement + { + AttributeName = "event_slug", + KeyType = "HASH" //Partition key + } + }, + { + new KeySchemaElement + { + AttributeName = "subscribe_as_of", + KeyType = "RANGE" //Sort key + } + } + }, + Projection = new Projection() + { + ProjectionType = ProjectionType.ALL + }, + ProvisionedThroughput = new ProvisionedThroughput() + { + ReadCapacityUnits = 1, + WriteCapacityUnits = 1 + } + }; + + var createRequest = new CreateTableRequest($"{_tablePrefix}-{DynamoPersistenceProvider.SUBCRIPTION_TABLE}", new List() + { + new KeySchemaElement("id", KeyType.HASH) + }) + { + AttributeDefinitions = new List() + { + new AttributeDefinition("id", ScalarAttributeType.S), + new AttributeDefinition("event_slug", ScalarAttributeType.S), + new AttributeDefinition("subscribe_as_of", ScalarAttributeType.N) + }, + GlobalSecondaryIndexes = new List() + { + slugIndex + }, + ProvisionedThroughput = new ProvisionedThroughput() + { + ReadCapacityUnits = 1, + WriteCapacityUnits = 1 + } + }; + + await CreateTable(createRequest); + } + + private async Task CreateEventTable() + { + var slugIndex = new GlobalSecondaryIndex() + { + IndexName = "ix_slug", + KeySchema = new List() + { + { + new KeySchemaElement + { + AttributeName= "event_slug", + KeyType = "HASH" //Partition key + } + }, + { + new KeySchemaElement + { + AttributeName = "event_time", + KeyType = "RANGE" //Sort key + } + } + }, + Projection = new Projection() + { + ProjectionType = ProjectionType.KEYS_ONLY + }, + ProvisionedThroughput = new ProvisionedThroughput() + { + ReadCapacityUnits = 1, + WriteCapacityUnits = 1 + } + }; + + var processedIndex = new GlobalSecondaryIndex() + { + IndexName = "ix_not_processed", + KeySchema = new List() + { + { + new KeySchemaElement + { + AttributeName = "not_processed", + KeyType = "HASH" //Partition key + } + }, + { + new KeySchemaElement + { + AttributeName = "event_time", + KeyType = "RANGE" //Sort key + } + } + }, + Projection = new Projection() + { + ProjectionType = ProjectionType.KEYS_ONLY + }, + ProvisionedThroughput = new ProvisionedThroughput() + { + ReadCapacityUnits = 1, + WriteCapacityUnits = 1 + } + }; + + var createRequest = new CreateTableRequest($"{_tablePrefix}-{DynamoPersistenceProvider.EVENT_TABLE}", new List() + { + new KeySchemaElement("id", KeyType.HASH) + }) + { + AttributeDefinitions = new List() + { + new AttributeDefinition("id", ScalarAttributeType.S), + new AttributeDefinition("not_processed", ScalarAttributeType.N), + new AttributeDefinition("event_slug", ScalarAttributeType.S), + new AttributeDefinition("event_time", ScalarAttributeType.N) + }, + GlobalSecondaryIndexes = new List() + { + slugIndex, + processedIndex + }, + ProvisionedThroughput = new ProvisionedThroughput() + { + ReadCapacityUnits = 1, + WriteCapacityUnits = 1 + } + }; + + await CreateTable(createRequest); + } + + private async Task EnsureTable(string tableName, Func createTask) + { + try + { + await _client.DescribeTableAsync(tableName); + } + catch (ResourceNotFoundException) + { + _logger.LogWarning($"Provisioning DynamoDb table - {tableName}"); + await createTask(); + } + } + + private async Task CreateTable(CreateTableRequest createRequest) + { + var createResponse = await _client.CreateTableAsync(createRequest); + + int i = 0; + bool created = false; + while ((i < 30) && (!created)) + { + try + { + await Task.Delay(2000); + var poll = await _client.DescribeTableAsync(createRequest.TableName); + created = (poll.Table.TableStatus == TableStatus.ACTIVE); + i++; + } + catch (ResourceNotFoundException) + { + } + } + } + } +} + diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs index 74aaf240e..489235133 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -42,7 +42,7 @@ public async Task AcquireLock(string Id, CancellationToken cancellationTok Item = new Dictionary { { "id", new AttributeValue(Id) }, - { "lockOwner", new AttributeValue(_nodeId) }, + { "lock_owner", new AttributeValue(_nodeId) }, { "expires", new AttributeValue() { N = Convert.ToString(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + _ttl) @@ -86,7 +86,7 @@ public async Task ReleaseLock(string Id) { { "id", new AttributeValue(Id) } }, - ConditionExpression = "lockOwner = :nodeId", + ConditionExpression = "lock_owner = :nodeId", ExpressionAttributeValues = new Dictionary { { ":nodeId", new AttributeValue(_nodeId) } @@ -137,14 +137,14 @@ private async void SendHeartbeat() Item = new Dictionary { { "id", new AttributeValue(item) }, - { "lockOwner", new AttributeValue(_nodeId) }, + { "lock_owner", new AttributeValue(_nodeId) }, { "expires", new AttributeValue() { N = Convert.ToString(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + _ttl) } } }, - ConditionExpression = "lockOwner = :nodeId", + ConditionExpression = "lock_owner = :nodeId", ExpressionAttributeValues = new Dictionary { { ":nodeId", new AttributeValue(_nodeId) } @@ -191,7 +191,7 @@ private async Task CreateTable() int i = 0; bool created = false; - while ((i < 10) && (!created)) + while ((i < 20) && (!created)) { try { diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs new file mode 100644 index 000000000..28e354509 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -0,0 +1,333 @@ +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using Amazon.Runtime; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Amazon.Util; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Providers.AWS.Services +{ + public class DynamoPersistenceProvider : IPersistenceProvider + { + private readonly ILogger _logger; + private readonly AmazonDynamoDBClient _client; + private readonly string _tablePrefix; + private readonly IDynamoDbProvisioner _provisioner; + + public const string WORKFLOW_TABLE = "workflows"; + public const string SUBCRIPTION_TABLE = "subscriptions"; + public const string EVENT_TABLE = "events"; + + public DynamoPersistenceProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, IDynamoDbProvisioner provisioner, string tablePrefix, ILoggerFactory logFactory) + { + _logger = logFactory.CreateLogger(); + _client = new AmazonDynamoDBClient(credentials, config); + _tablePrefix = tablePrefix; + _provisioner = provisioner; + } + + public async Task CreateNewWorkflow(WorkflowInstance workflow) + { + workflow.Id = Guid.NewGuid().ToString(); + + var req = new PutItemRequest() + { + TableName = $"{_tablePrefix}-{WORKFLOW_TABLE}", + Item = workflow.ToDynamoMap(), + ConditionExpression = "attribute_not_exists(id)" + }; + + var response = await _client.PutItemAsync(req); + + return workflow.Id; + } + + public async Task PersistWorkflow(WorkflowInstance workflow) + { + var request = new PutItemRequest() + { + TableName = $"{_tablePrefix}-{WORKFLOW_TABLE}", + Item = workflow.ToDynamoMap() + }; + + var response = await _client.PutItemAsync(request); + } + + public async Task> GetRunnableInstances(DateTime asAt) + { + var result = new List(); + var now = asAt.ToUniversalTime().Ticks; + + var request = new QueryRequest() + { + TableName = $"{_tablePrefix}-{WORKFLOW_TABLE}", + IndexName = "ix_runnable", + ProjectionExpression = "id", + KeyConditionExpression = "runnable = :r and next_execution <= :effective_date", + ExpressionAttributeValues = new Dictionary + { + { + ":r", new AttributeValue() + { + N = 1.ToString() + } + }, + { + ":effective_date", new AttributeValue() + { + N = Convert.ToString(now) + } + } + }, + ScanIndexForward = true + }; + + var response = await _client.QueryAsync(request); + + foreach (var item in response.Items) + { + result.Add(item["id"].S); + } + + return result; + } + + public Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) + { + throw new NotImplementedException(); + } + + public async Task GetWorkflowInstance(string Id) + { + var req = new GetItemRequest() + { + TableName = $"{_tablePrefix}-{WORKFLOW_TABLE}", + Key = new Dictionary + { + { "id", new AttributeValue(Id) } + } + }; + var response = await _client.GetItemAsync(req); + + return response.Item.ToWorkflowInstance(); + } + + public async Task CreateEventSubscription(EventSubscription subscription) + { + subscription.Id = Guid.NewGuid().ToString(); + + var req = new PutItemRequest() + { + TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", + Item = subscription.ToDynamoMap(), + ConditionExpression = "attribute_not_exists(id)" + }; + + var response = await _client.PutItemAsync(req); + + return subscription.Id; + } + + public async Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) + { + var result = new List(); + var asOfTicks = asOf.ToUniversalTime().Ticks; + + var request = new QueryRequest() + { + TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", + IndexName = "ix_slug", + Select = "ALL_PROJECTED_ATTRIBUTES", + KeyConditionExpression = "event_slug = :slug and subscribe_as_of <= :as_of", + ExpressionAttributeValues = new Dictionary + { + { + ":slug", new AttributeValue($"{eventName}:{eventKey}") + }, + { + ":as_of", new AttributeValue() + { + N = Convert.ToString(asOfTicks) + } + } + }, + ScanIndexForward = true + }; + + var response = await _client.QueryAsync(request); + + foreach (var item in response.Items) + { + result.Add(item.ToEventSubscription()); + } + + return result; + } + + public async Task TerminateSubscription(string eventSubscriptionId) + { + var request = new DeleteItemRequest() + { + TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", + Key = new Dictionary + { + { "id", new AttributeValue(eventSubscriptionId) } + } + }; + await _client.DeleteItemAsync(request); + } + + public async Task CreateEvent(Event newEvent) + { + newEvent.Id = Guid.NewGuid().ToString(); + + var req = new PutItemRequest() + { + TableName = $"{_tablePrefix}-{EVENT_TABLE}", + Item = newEvent.ToDynamoMap(), + ConditionExpression = "attribute_not_exists(id)" + }; + + var response = await _client.PutItemAsync(req); + + return newEvent.Id; + } + + public async Task GetEvent(string id) + { + var req = new GetItemRequest() + { + TableName = $"{_tablePrefix}-{EVENT_TABLE}", + Key = new Dictionary + { + { "id", new AttributeValue(id) } + } + }; + var response = await _client.GetItemAsync(req); + + return response.Item.ToEvent(); + } + + public async Task> GetRunnableEvents(DateTime asAt) + { + var result = new List(); + var now = asAt.ToUniversalTime().Ticks; + + var request = new QueryRequest() + { + TableName = $"{_tablePrefix}-{EVENT_TABLE}", + IndexName = "ix_not_processed", + ProjectionExpression = "id", + KeyConditionExpression = "not_processed = :n and event_time <= :effectiveDate", + ExpressionAttributeValues = new Dictionary + { + { ":n" , new AttributeValue() { N = 1.ToString() } }, + { + ":effectiveDate", new AttributeValue() + { + N = Convert.ToString(now) + } + } + }, + ScanIndexForward = true + }; + + var response = await _client.QueryAsync(request); + + foreach (var item in response.Items) + { + result.Add(item["id"].S); + } + + return result; + } + + public async Task> GetEvents(string eventName, string eventKey, DateTime asOf) + { + var result = new List(); + var asOfTicks = asOf.ToUniversalTime().Ticks; + + var request = new QueryRequest() + { + TableName = $"{_tablePrefix}-{EVENT_TABLE}", + IndexName = "ix_slug", + ProjectionExpression = "id", + KeyConditionExpression = "event_slug = :slug and event_time >= :effective_date", + ExpressionAttributeValues = new Dictionary + { + { + ":slug", new AttributeValue($"{eventName}:{eventKey}") + }, + { + ":effective_date", new AttributeValue() + { + N = Convert.ToString(asOfTicks) + } + } + }, + ScanIndexForward = true + }; + + var response = await _client.QueryAsync(request); + + foreach (var item in response.Items) + { + result.Add(item["id"].S); + } + + return result; + } + + public async Task MarkEventProcessed(string id) + { + var request = new UpdateItemRequest() + { + TableName = $"{_tablePrefix}-{EVENT_TABLE}", + Key = new Dictionary + { + { "id", new AttributeValue(id) } + }, + UpdateExpression = "REMOVE not_processed", + //ExpressionAttributeValues = new Dictionary() + //{ + // { ":processed" , new AttributeValue(true.ToString()) } + //} + }; + await _client.UpdateItemAsync(request); + } + + public async Task MarkEventUnprocessed(string id) + { + var request = new UpdateItemRequest() + { + TableName = $"{_tablePrefix}-{EVENT_TABLE}", + Key = new Dictionary + { + { "id", new AttributeValue(id) } + }, + UpdateExpression = "ADD not_processed = :n", + ExpressionAttributeValues = new Dictionary() + { + { ":n" , new AttributeValue() { N = 1.ToString() } } + } + }; + await _client.UpdateItemAsync(request); + } + + public Task PersistErrors(IEnumerable errors) + { + //TODO + return Task.CompletedTask; + } + + public void EnsureStoreExists() + { + _provisioner.ProvisionTables().Wait(); + } + } +} diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/IDynamoDbProvisioner.cs b/src/providers/WorkflowCore.Providers.AWS/Services/IDynamoDbProvisioner.cs new file mode 100644 index 000000000..25498e4ae --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/Services/IDynamoDbProvisioner.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace WorkflowCore.Providers.AWS.Services +{ + public interface IDynamoDbProvisioner + { + Task ProvisionTables(); + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 2954acd97..5e453fc6d 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,14 +11,15 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 1.6.9 - 1.6.9.0 + 1.6.11 + 1.6.11.0 + diff --git a/src/samples/WorkflowCore.Sample04/Program.cs b/src/samples/WorkflowCore.Sample04/Program.cs index a7c242b85..7eabd55ec 100644 --- a/src/samples/WorkflowCore.Sample04/Program.cs +++ b/src/samples/WorkflowCore.Sample04/Program.cs @@ -63,8 +63,11 @@ private static IServiceProvider ConfigureServices() //services.AddWorkflow(cfg => //{ - // cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); - // cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); + // var ddbConfig = new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }; + + // cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), ddbConfig, "sample4"); + // cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), ddbConfig, "workflow-core-locks"); + // cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); //}); //services.AddWorkflow(x => x.UseRedlock(new System.Net.DnsEndPoint("127.0.0.1", 32768))); diff --git a/test/Docker.Testify/Docker.Testify.csproj b/test/Docker.Testify/Docker.Testify.csproj index 36c58c385..8d4989b7c 100644 --- a/test/Docker.Testify/Docker.Testify.csproj +++ b/test/Docker.Testify/Docker.Testify.csproj @@ -1,11 +1,11 @@  - netstandard1.4 + netstandard2.0 - + diff --git a/test/Docker.Testify/DockerSetup.cs b/test/Docker.Testify/DockerSetup.cs index eb175b22c..0bf7055df 100644 --- a/test/Docker.Testify/DockerSetup.cs +++ b/test/Docker.Testify/DockerSetup.cs @@ -110,16 +110,11 @@ public async Task PullImage(string name, string tag) if (exists) return; - var stream = await docker.Images.PullImageAsync(new ImagesPullParameters() { Parent = name, Tag = tag }, null); - - using (StreamReader reader = new StreamReader(stream)) - { - while (!reader.EndOfStream) - Debug.WriteLine(reader.ReadLine()); - } + Debug.WriteLine($"Pulling docker image {name}:{tag}"); + await docker.Images.CreateImageAsync(new ImagesCreateParameters() { FromImage = name, Tag = tag }, null, new Progress()); } - public void Dispose() + public void Dispose() { docker.Containers.KillContainerAsync(containerId, new ContainerKillParameters()).Wait(); docker.Containers.RemoveContainerAsync(containerId, new ContainerRemoveParameters() { Force = true }).Wait(); diff --git a/test/ScratchPad/Program.cs b/test/ScratchPad/Program.cs index 31a869b92..6f11d9a39 100644 --- a/test/ScratchPad/Program.cs +++ b/test/ScratchPad/Program.cs @@ -8,6 +8,9 @@ using WorkflowCore.Interface; using WorkflowCore.Models; using System.Text; +using Amazon; +using Amazon.DynamoDBv2; +using Amazon.Runtime; namespace ScratchPad { @@ -22,19 +25,29 @@ public static void Main(string[] args) //start the workflow host var host = serviceProvider.GetService(); - var loader = serviceProvider.GetService(); - var str = ScratchPad.Properties.Resources.HelloWorld; //Encoding.UTF8.GetString(ScratchPad.Properties.Resources.HelloWorld); - - loader.LoadDefinition(str); - + var persistence = serviceProvider.GetService(); + + var wf = new WorkflowInstance() + { + Description = "test", + CreateTime = DateTime.UtcNow, + Status = WorkflowStatus.Terminated, + Version = 1, + WorkflowDefinitionId = "def" + }; + var id = persistence.CreateNewWorkflow(wf).Result; + + //var loader = serviceProvider.GetService(); + //var str = ScratchPad.Properties.Resources.HelloWorld; //Encoding.UTF8.GetString(ScratchPad.Properties.Resources.HelloWorld); + //loader.LoadDefinition(str); //host.RegisterWorkflow(); - host.Start(); + //host.Start(); + + //host.StartWorkflow("HelloWorld", 1, new MyDataClass() { Value3 = "hi there" }); - host.StartWorkflow("HelloWorld", 1, new MyDataClass() { Value3 = "hi there" }); - Console.ReadLine(); - host.Stop(); + //host.Stop(); } private static IServiceProvider ConfigureServices() @@ -42,8 +55,17 @@ private static IServiceProvider ConfigureServices() //setup dependency injection IServiceCollection services = new ServiceCollection(); services.AddLogging(); - services.AddWorkflow(); + //services.AddWorkflow(); //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); + services.AddWorkflow(cfg => + { + var ddbConfig = new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }; + cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), ddbConfig, "sample31"); + //cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); + //cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); + }); + + services.AddTransient(); var serviceProvider = services.BuildServiceProvider(); diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 0154c5b2a..94f5d773f 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -13,10 +13,12 @@ + + diff --git a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs index 9abe7fbf2..04be3a602 100644 --- a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs @@ -12,6 +12,8 @@ public class MysqlDockerSetup : DockerSetup public static string ScenarioConnectionString { get; set; } public static string RootPassword => "rootpwd123"; + public override TimeSpan TimeOut => TimeSpan.FromSeconds(60); + public override string ImageTag => "5.7.24"; public override string ImageName => "mysql"; public override IList EnvironmentVariables => new List { diff --git a/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs b/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs index 5ded402f5..6b589666d 100644 --- a/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs +++ b/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs @@ -18,7 +18,7 @@ public class SqlDockerSetup : DockerSetup public override string ImageName => "microsoft/mssql-server-linux"; public override int InternalPort => 1433; - public override TimeSpan TimeOut => TimeSpan.FromSeconds(60); + public override TimeSpan TimeOut => TimeSpan.FromSeconds(120); public const string SqlPassword = "I@mJustT3st1ing"; From b09e07ec6960a13c8535f656da6b9abfa2e9ee32 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 13 Jan 2019 08:30:03 -0800 Subject: [PATCH 085/462] Workflow Core v1.7 (#237) --- ReleaseNotes/1.7.0.md | 80 ++++++++++++++ WorkflowCore.sln | 20 +++- .../Interface/ICancellationProcessor.cs | 12 ++ .../Interface/ILifeCycleEventHub.cs | 2 + src/WorkflowCore/Interface/IStepBuilder.cs | 7 ++ src/WorkflowCore/Models/ExecutionPointer.cs | 18 ++- .../Models/ExecutionPointerCollection.cs | 103 ++++++++++++++++++ src/WorkflowCore/Models/WorkflowInstance.cs | 11 +- src/WorkflowCore/Models/WorkflowStep.cs | 6 + .../Primitives/CancellableStep.cs | 29 ----- .../Primitives/ContainerStepBody.cs | 27 +---- src/WorkflowCore/Primitives/Foreach.cs | 8 +- src/WorkflowCore/Primitives/If.cs | 8 +- src/WorkflowCore/Primitives/OutcomeSwitch.cs | 12 +- src/WorkflowCore/Primitives/Schedule.cs | 11 +- src/WorkflowCore/Primitives/Sequence.cs | 6 +- src/WorkflowCore/Primitives/When.cs | 10 +- src/WorkflowCore/Primitives/While.cs | 7 +- .../ServiceCollectionExtensions.cs | 1 + .../Services/CancellationProcessor.cs | 62 +++++++++++ .../DefaultProviders/SingleNodeEventHub.cs | 11 ++ .../DefinitionStorage/DefinitionLoader.cs | 15 ++- .../ErrorHandlers/CompensateHandler.cs | 4 +- .../Services/ExecutionPointerFactory.cs | 6 +- .../Services/ExecutionResultProcessor.cs | 2 +- .../Services/FluentBuilders/StepBuilder.cs | 27 ++--- .../Services/WorkflowController.cs | 5 +- src/WorkflowCore/Services/WorkflowExecutor.cs | 10 +- src/WorkflowCore/Services/WorkflowHost.cs | 13 ++- src/WorkflowCore/WorkflowCore.csproj | 8 +- .../WorkflowCore.Users/Primitives/Escalate.cs | 2 +- .../Primitives/EscalateStep.cs | 2 +- .../WorkflowCore.Users/Primitives/UserTask.cs | 6 +- .../WorkflowCore.Users.csproj | 6 +- .../WorkflowCore.WebAPI.csproj | 2 +- .../WorkflowCore.LockProviders.Redlock.csproj | 6 +- ...orkflowCore.LockProviders.SqlServer.csproj | 2 +- .../ExtensionMethods.cs | 18 +-- .../PersistedExecutionPointerCollection.cs | 68 ++++++++++++ .../Models/PersistedWorkflow.cs | 3 +- ...lowCore.Persistence.EntityFramework.csproj | 8 +- .../WorkflowCore.Persistence.MongoDB.csproj | 12 +- .../WorkflowCore.Persistence.MySQL.csproj | 6 +- ...WorkflowCore.Persistence.PostgreSQL.csproj | 6 +- .../WorkflowCore.Persistence.SqlServer.csproj | 6 +- .../WorkflowCore.Persistence.Sqlite.csproj | 6 +- .../WorkflowCore.Providers.AWS/README.md | 4 +- .../Services/DynamoLockProvider.cs | 63 ++++++++--- .../Services/DynamoPersistenceProvider.cs | 6 +- .../WorkflowCore.Providers.AWS.csproj | 8 +- .../WorkflowCore.Providers.Azure.csproj | 8 +- .../WorkflowCore.Providers.Redis/README.md | 37 +++++++ .../ServiceCollectionExtensions.cs | 29 +++++ .../Services/RedisLifeCycleEventHub.cs | 77 +++++++++++++ .../Services/RedisLockProvider.cs | 84 ++++++++++++++ .../Services/RedisQueueProvider.cs | 85 +++++++++++++++ .../WorkflowCore.Providers.Redis.csproj | 27 +++++ ...orkflowCore.QueueProviders.RabbitMQ.csproj | 8 +- ...rkflowCore.QueueProviders.SqlServer.csproj | 2 +- src/samples/WorkflowCore.Sample04/Program.cs | 7 +- .../WorkflowCore.Sample04.csproj | 2 +- .../WorkflowCore.Sample13/ParallelWorkflow.cs | 4 +- src/samples/WorkflowCore.Sample13/Program.cs | 2 +- test/ScratchPad/ScratchPad.csproj | 2 +- .../WorkflowCore.TestAssets.csproj | 2 +- .../WorkflowCore.Testing.csproj | 6 +- .../DynamoDbDockerSetup.cs | 0 .../DynamoPersistenceProviderFixture.cs | 0 .../Scenarios/DynamoBasicScenario.cs | 0 .../Scenarios/DynamoCompensationScenario.cs | 0 .../Scenarios/DynamoDataScenario.cs | 0 .../Scenarios/DynamoDynamicDataScenario.cs | 0 .../Scenarios/DynamoEventScenario.cs | 0 .../Scenarios/DynamoForeachScenario.cs | 0 .../Scenarios/DynamoIfScenario.cs | 0 .../Scenarios/DynamoRetrySagaScenario.cs | 0 .../Scenarios/DynamoSagaScenario.cs | 0 .../Scenarios/DynamoWhileScenario.cs | 0 .../WorkflowCore.Tests.DynamoDB.csproj | 10 +- .../WorkflowCore.Tests.MongoDB.csproj | 2 +- .../BasePersistenceFixture.cs | 10 +- .../ExecutionResultProcessorFixture.cs | 25 ++--- .../Services/WorkflowExecutorFixture.cs | 87 ++++++++++----- 83 files changed, 999 insertions(+), 298 deletions(-) create mode 100644 ReleaseNotes/1.7.0.md create mode 100644 src/WorkflowCore/Interface/ICancellationProcessor.cs create mode 100644 src/WorkflowCore/Models/ExecutionPointerCollection.cs delete mode 100644 src/WorkflowCore/Primitives/CancellableStep.cs create mode 100644 src/WorkflowCore/Services/CancellationProcessor.cs create mode 100644 src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointerCollection.cs create mode 100644 src/providers/WorkflowCore.Providers.Redis/README.md create mode 100644 src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs create mode 100644 src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs create mode 100644 src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs create mode 100644 src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs create mode 100644 src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/DynamoDbDockerSetup.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/DynamoPersistenceProviderFixture.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/Scenarios/DynamoBasicScenario.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/Scenarios/DynamoCompensationScenario.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/Scenarios/DynamoDataScenario.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/Scenarios/DynamoDynamicDataScenario.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/Scenarios/DynamoEventScenario.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/Scenarios/DynamoForeachScenario.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/Scenarios/DynamoIfScenario.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/Scenarios/DynamoRetrySagaScenario.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/Scenarios/DynamoSagaScenario.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/Scenarios/DynamoWhileScenario.cs (100%) rename {WorkflowCore.Tests.DynamoDB => test/WorkflowCore.Tests.DynamoDB}/WorkflowCore.Tests.DynamoDB.csproj (55%) diff --git a/ReleaseNotes/1.7.0.md b/ReleaseNotes/1.7.0.md new file mode 100644 index 000000000..d4bef0b4b --- /dev/null +++ b/ReleaseNotes/1.7.0.md @@ -0,0 +1,80 @@ +# Workflow Core 1.7.0 + +* Various performance optimizations, any users of the EntityFramework persistence providers will have to update their persistence libraries to the latest version as well. +* Added `CancelCondition` to fluent builder API. + + ``` + .CancelCondition(data => <>, <>) + + ``` + + This allows you to specify a condition under which any active step can be prematurely cancelled. + For example, suppose you create a future scheduled task, but you want to cancel the future execution of this task if some condition becomes true. + + + ```c# + builder + .StartWith(context => Console.WriteLine("Hello")) + .Schedule(data => TimeSpan.FromSeconds(5)).Do(schedule => schedule + .StartWith() + .Then() + ) + .CancelCondition(data => !data.SheduledTaskRequired) + .Then(context => Console.WriteLine("Doing normal tasks")); + ``` + + You could also use this implement a parallel flow where once a single path completes, all the other paths are cancelled. + + ```c# + .Parallel() + .Do(then => then + .StartWith() + .WaitFor("Approval", (data, context) => context.Workflow.IdNow) + ) + .Do(then => then + .StartWith() + .Delay(data => TimeSpan.FromDays(3)) + .Then() + ) + .Join() + .CancelCondition(data => data.IsApproved, true) + .Then(); + ``` + +* Deprecated `WorkflowCore.LockProviders.RedLock` in favour of `WorkflowCore.Providers.Redis` +* Create a new `WorkflowCore.Providers.Redis` library that includes providers for distributed locking, queues and event hubs. + * Provides Queueing support backed by Redis. + * Provides Distributed locking support backed by Redis. + * Provides event hub support backed by Redis. + + This makes it possible to have a cluster of nodes processing your workflows. + + ## Installing + + Install the NuGet package "WorkflowCore.Providers.Redis" + + Using Nuget package console + ``` + PM> Install-Package WorkflowCore.Providers.Redis + ``` + Using .NET CLI + ``` + dotnet add package WorkflowCore.Providers.Redis + ``` + + + ## Usage + + Use the `IServiceCollection` extension methods when building your service provider + * .UseRedisQueues + * .UseRedisLocking + * .UseRedisEventHub + + ```C# + services.AddWorkflow(cfg => + { + cfg.UseRedisLocking("localhost:6379"); + cfg.UseRedisQueues("localhost:6379", "my-app"); + cfg.UseRedisEventHub("localhost:6379", "my-channel") + }); + ``` diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 30a4b7930..a95be36f8 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -94,6 +94,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.6.6.md = ReleaseNotes\1.6.6.md ReleaseNotes\1.6.8.md = ReleaseNotes\1.6.8.md ReleaseNotes\1.6.9.md = ReleaseNotes\1.6.9.md + ReleaseNotes\1.7.0.md = ReleaseNotes\1.7.0.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" @@ -120,7 +121,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Persistence.My EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.MySQL", "test\WorkflowCore.Tests.MySQL\WorkflowCore.Tests.MySQL.csproj", "{DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.DynamoDB", "WorkflowCore.Tests.DynamoDB\WorkflowCore.Tests.DynamoDB.csproj", "{0FF3F27E-E909-4ABA-BF82-39D1BA133EA7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.DynamoDB", "test\WorkflowCore.Tests.DynamoDB\WorkflowCore.Tests.DynamoDB.csproj", "{3ECEC028-7E2C-4983-B928-26C073B51BB7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Providers.Redis", "src\providers\WorkflowCore.Providers.Redis\WorkflowCore.Providers.Redis.csproj", "{435C6263-C6F8-4E93-B417-D861E9C22E18}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -304,10 +307,14 @@ Global {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE}.Release|Any CPU.Build.0 = Release|Any CPU - {0FF3F27E-E909-4ABA-BF82-39D1BA133EA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0FF3F27E-E909-4ABA-BF82-39D1BA133EA7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0FF3F27E-E909-4ABA-BF82-39D1BA133EA7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0FF3F27E-E909-4ABA-BF82-39D1BA133EA7}.Release|Any CPU.Build.0 = Release|Any CPU + {3ECEC028-7E2C-4983-B928-26C073B51BB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3ECEC028-7E2C-4983-B928-26C073B51BB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3ECEC028-7E2C-4983-B928-26C073B51BB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3ECEC028-7E2C-4983-B928-26C073B51BB7}.Release|Any CPU.Build.0 = Release|Any CPU + {435C6263-C6F8-4E93-B417-D861E9C22E18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {435C6263-C6F8-4E93-B417-D861E9C22E18}.Debug|Any CPU.Build.0 = Debug|Any CPU + {435C6263-C6F8-4E93-B417-D861E9C22E18}.Release|Any CPU.ActiveCfg = Release|Any CPU + {435C6263-C6F8-4E93-B417-D861E9C22E18}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -360,7 +367,8 @@ Global {5E82A137-0954-46A1-8C46-13C00F0E4842} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {453E260D-DBDC-4DDC-BC9C-CA500CED7897} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} - {0FF3F27E-E909-4ABA-BF82-39D1BA133EA7} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} + {3ECEC028-7E2C-4983-B928-26C073B51BB7} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} + {435C6263-C6F8-4E93-B417-D861E9C22E18} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/WorkflowCore/Interface/ICancellationProcessor.cs b/src/WorkflowCore/Interface/ICancellationProcessor.cs new file mode 100644 index 000000000..09d37969b --- /dev/null +++ b/src/WorkflowCore/Interface/ICancellationProcessor.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface ICancellationProcessor + { + void ProcessCancellations(WorkflowInstance workflow, WorkflowDefinition workflowDef, WorkflowExecutorResult executionResult); + } +} diff --git a/src/WorkflowCore/Interface/ILifeCycleEventHub.cs b/src/WorkflowCore/Interface/ILifeCycleEventHub.cs index 767529841..70663effc 100644 --- a/src/WorkflowCore/Interface/ILifeCycleEventHub.cs +++ b/src/WorkflowCore/Interface/ILifeCycleEventHub.cs @@ -10,5 +10,7 @@ public interface ILifeCycleEventHub { Task PublishNotification(LifeCycleEvent evt); void Subscribe(Action action); + Task Start(); + Task Stop(); } } diff --git a/src/WorkflowCore/Interface/IStepBuilder.cs b/src/WorkflowCore/Interface/IStepBuilder.cs index a9fc7b299..aedd10b2e 100644 --- a/src/WorkflowCore/Interface/IStepBuilder.cs +++ b/src/WorkflowCore/Interface/IStepBuilder.cs @@ -213,6 +213,13 @@ public interface IStepBuilder /// /// IStepBuilder CompensateWithSequence(Action> builder); + + /// + /// Prematurely cancel the execution of this step on a condition + /// + /// + /// + IStepBuilder CancelCondition(Expression> cancelCondition, bool proceedAfterCancel = false); } } \ No newline at end of file diff --git a/src/WorkflowCore/Models/ExecutionPointer.cs b/src/WorkflowCore/Models/ExecutionPointer.cs index 013c130e6..53938d76a 100644 --- a/src/WorkflowCore/Models/ExecutionPointer.cs +++ b/src/WorkflowCore/Models/ExecutionPointer.cs @@ -6,6 +6,8 @@ namespace WorkflowCore.Models { public class ExecutionPointer { + private IReadOnlyCollection _scope = new List(); + public string Id { get; set; } public int StepId { get; set; } @@ -26,8 +28,8 @@ public class ExecutionPointer public bool EventPublished { get; set; } - public object EventData { get; set; } - + public object EventData { get; set; } + public Dictionary ExtensionAttributes { get; set; } = new Dictionary(); public string StepName { get; set; } @@ -43,9 +45,12 @@ public class ExecutionPointer public object Outcome { get; set; } public PointerStatus Status { get; set; } = PointerStatus.Legacy; - - public Stack Scope { get; set; } = new Stack(); - + + public IReadOnlyCollection Scope + { + get => _scope; + set => _scope = new List(value); + } } public enum PointerStatus @@ -57,6 +62,7 @@ public enum PointerStatus Sleeping = 4, WaitingForEvent = 5, Failed = 6, - Compensated = 7 + Compensated = 7, + Cancelled = 8 } } diff --git a/src/WorkflowCore/Models/ExecutionPointerCollection.cs b/src/WorkflowCore/Models/ExecutionPointerCollection.cs new file mode 100644 index 000000000..a81e9fcf8 --- /dev/null +++ b/src/WorkflowCore/Models/ExecutionPointerCollection.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WorkflowCore.Models +{ + public class ExecutionPointerCollection : ICollection + { + private readonly Dictionary _dictionary = new Dictionary(); + private readonly Dictionary> _scopeMap = new Dictionary>(); + + public ExecutionPointerCollection() + { + } + + public ExecutionPointerCollection(int capacity) + { + _dictionary = new Dictionary(capacity); + } + + public ExecutionPointerCollection(ICollection pointers) + { + foreach (var ptr in pointers) + { + Add(ptr); + } + } + + public IEnumerator GetEnumerator() + { + return _dictionary.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public ExecutionPointer FindById(string id) + { + if (!_dictionary.ContainsKey(id)) + return null; + + return _dictionary[id]; + } + + public ICollection FindByScope(string stackFrame) + { + if (!_scopeMap.ContainsKey(stackFrame)) + return new List(); + + return _scopeMap[stackFrame]; + } + + public void Add(ExecutionPointer item) + { + _dictionary.Add(item.Id, item); + + foreach (var stackFrame in item.Scope) + { + if (!_scopeMap.ContainsKey(stackFrame)) + _scopeMap.Add(stackFrame, new List()); + _scopeMap[stackFrame].Add(item); + } + } + + public void Clear() + { + _dictionary.Clear(); + _scopeMap.Clear(); + } + + public bool Contains(ExecutionPointer item) + { + return _dictionary.ContainsValue(item); + } + + public void CopyTo(ExecutionPointer[] array, int arrayIndex) + { + _dictionary.Values.CopyTo(array, arrayIndex); + } + + public bool Remove(ExecutionPointer item) + { + foreach (var stackFrame in item.Scope) + { + _scopeMap[stackFrame].Remove(item); + } + + return _dictionary.Remove(item.Id); + } + + public ExecutionPointer Find(Predicate match) + { + return _dictionary.Values.FirstOrDefault(x => match(x)); + } + + public int Count => _dictionary.Count; + public bool IsReadOnly => false; + } +} diff --git a/src/WorkflowCore/Models/WorkflowInstance.cs b/src/WorkflowCore/Models/WorkflowInstance.cs index 402dbc317..b75b284fc 100644 --- a/src/WorkflowCore/Models/WorkflowInstance.cs +++ b/src/WorkflowCore/Models/WorkflowInstance.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace WorkflowCore.Models { @@ -15,7 +16,7 @@ public class WorkflowInstance public string Reference { get; set; } - public List ExecutionPointers { get; set; } = new List(); + public ExecutionPointerCollection ExecutionPointers { get; set; } = new ExecutionPointerCollection(); public long? NextExecution { get; set; } @@ -25,8 +26,14 @@ public class WorkflowInstance public DateTime CreateTime { get; set; } - public DateTime? CompleteTime { get; set; } + public DateTime? CompleteTime { get; set; } + public bool IsBranchComplete(string parentId) + { + return ExecutionPointers + .FindByScope(parentId) + .All(x => x.EndTime != null); + } } public enum WorkflowStatus diff --git a/src/WorkflowCore/Models/WorkflowStep.cs b/src/WorkflowCore/Models/WorkflowStep.cs index 73ddb0561..1ef0db10c 100644 --- a/src/WorkflowCore/Models/WorkflowStep.cs +++ b/src/WorkflowCore/Models/WorkflowStep.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq.Expressions; using System.Reflection; using WorkflowCore.Interface; @@ -33,6 +34,10 @@ public abstract class WorkflowStep public virtual bool RevertChildrenAfterCompensation => false; + public virtual LambdaExpression CancelCondition { get; set; } + + public bool ProceedOnCancel { get; set; } = false; + public virtual ExecutionPipelineDirective InitForExecution(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { return ExecutionPipelineDirective.Next; @@ -61,6 +66,7 @@ public virtual void PrimeForRetry(ExecutionPointer pointer) /// public virtual void AfterWorkflowIteration(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { + } public virtual IStepBody ConstructBody(IServiceProvider serviceProvider) diff --git a/src/WorkflowCore/Primitives/CancellableStep.cs b/src/WorkflowCore/Primitives/CancellableStep.cs deleted file mode 100644 index 8c4a960eb..000000000 --- a/src/WorkflowCore/Primitives/CancellableStep.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Linq.Expressions; -using WorkflowCore.Interface; -using WorkflowCore.Models; - -namespace WorkflowCore.Primitives -{ - public class CancellableStep : WorkflowStep - where TStepBody : IStepBody - { - private readonly Expression> _cancelCondition; - - public CancellableStep(Expression> cancelCondition) - { - _cancelCondition = cancelCondition; - } - - public override void AfterWorkflowIteration(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) - { - base.AfterWorkflowIteration(executorResult, defintion, workflow, executionPointer); - var func = _cancelCondition.Compile(); - if (func((TData) workflow.Data)) - { - executionPointer.EndTime = DateTime.Now.ToUniversalTime(); - executionPointer.Active = false; - } - } - } -} diff --git a/src/WorkflowCore/Primitives/ContainerStepBody.cs b/src/WorkflowCore/Primitives/ContainerStepBody.cs index fec393852..30ad9b644 100644 --- a/src/WorkflowCore/Primitives/ContainerStepBody.cs +++ b/src/WorkflowCore/Primitives/ContainerStepBody.cs @@ -6,31 +6,6 @@ namespace WorkflowCore.Primitives { public abstract class ContainerStepBody : StepBody { - protected bool IsBranchComplete(ICollection pointers, string rootId) - { - var root = pointers.First(x => x.Id == rootId); - - if (root.EndTime == null) - { - return false; - } - - var queue = new Queue(pointers.Where(x => x.PredecessorId == rootId)); - - while (queue.Count > 0) - { - var item = queue.Dequeue(); - if (item.EndTime == null) - { - return false; - } - - var children = pointers.Where(x => x.PredecessorId == item.Id).ToList(); - foreach (var child in children) - queue.Enqueue(child); - } - - return true; - } + } } diff --git a/src/WorkflowCore/Primitives/Foreach.cs b/src/WorkflowCore/Primitives/Foreach.cs index 9551ef871..23cecc947 100644 --- a/src/WorkflowCore/Primitives/Foreach.cs +++ b/src/WorkflowCore/Primitives/Foreach.cs @@ -22,13 +22,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if ((context.PersistenceData as ControlPersistenceData).ChildrenActive) { - bool complete = true; - foreach (var childId in context.ExecutionPointer.Children) - { - complete = complete && IsBranchComplete(context.Workflow.ExecutionPointers, childId); - } - - if (complete) + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) { return ExecutionResult.Next(); } diff --git a/src/WorkflowCore/Primitives/If.cs b/src/WorkflowCore/Primitives/If.cs index ba7f27820..2aaaad963 100644 --- a/src/WorkflowCore/Primitives/If.cs +++ b/src/WorkflowCore/Primitives/If.cs @@ -22,12 +22,8 @@ public override ExecutionResult Run(IStepExecutionContext context) } if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) - { - bool complete = true; - foreach (var childId in context.ExecutionPointer.Children) - complete = complete && IsBranchComplete(context.Workflow.ExecutionPointers, childId); - - if (complete) + { + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) { return ExecutionResult.Next(); } diff --git a/src/WorkflowCore/Primitives/OutcomeSwitch.cs b/src/WorkflowCore/Primitives/OutcomeSwitch.cs index 0ee1d5bda..74c2cf1de 100644 --- a/src/WorkflowCore/Primitives/OutcomeSwitch.cs +++ b/src/WorkflowCore/Primitives/OutcomeSwitch.cs @@ -18,14 +18,8 @@ public override ExecutionResult Run(IStepExecutionContext context) } if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) - { - bool complete = true; - foreach (var childId in context.ExecutionPointer.Children) - { - complete = complete && IsBranchComplete(context.Workflow.ExecutionPointers, childId); - } - - if (complete) + { + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) { return ExecutionResult.Next(); } @@ -42,7 +36,7 @@ public override ExecutionResult Run(IStepExecutionContext context) private object GetPreviousOutcome(IStepExecutionContext context) { - var prevPointer = context.Workflow.ExecutionPointers.First(x => x.Id == context.ExecutionPointer.PredecessorId); + var prevPointer = context.Workflow.ExecutionPointers.FindById(context.ExecutionPointer.PredecessorId); return prevPointer.Outcome; } } diff --git a/src/WorkflowCore/Primitives/Schedule.cs b/src/WorkflowCore/Primitives/Schedule.cs index 856187a53..9c74f2a97 100644 --- a/src/WorkflowCore/Primitives/Schedule.cs +++ b/src/WorkflowCore/Primitives/Schedule.cs @@ -22,15 +22,8 @@ public override ExecutionResult Run(IStepExecutionContext context) { return ExecutionResult.Branch(new List() { null }, new SchedulePersistenceData() { Elapsed = true }); } - - var complete = true; - - foreach (var childId in context.ExecutionPointer.Children) - { - complete = complete && IsBranchComplete(context.Workflow.ExecutionPointers, childId); - } - - if (complete) + + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) { return ExecutionResult.Next(); } diff --git a/src/WorkflowCore/Primitives/Sequence.cs b/src/WorkflowCore/Primitives/Sequence.cs index 66d543333..20140af47 100644 --- a/src/WorkflowCore/Primitives/Sequence.cs +++ b/src/WorkflowCore/Primitives/Sequence.cs @@ -16,11 +16,7 @@ public override ExecutionResult Run(IStepExecutionContext context) if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) { - bool complete = true; - foreach (var childId in context.ExecutionPointer.Children) - complete = complete && IsBranchComplete(context.Workflow.ExecutionPointers, childId); - - if (complete) + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) { return ExecutionResult.Next(); } diff --git a/src/WorkflowCore/Primitives/When.cs b/src/WorkflowCore/Primitives/When.cs index f70c113b0..ca9434f66 100644 --- a/src/WorkflowCore/Primitives/When.cs +++ b/src/WorkflowCore/Primitives/When.cs @@ -29,14 +29,8 @@ public override ExecutionResult Run(IStepExecutionContext context) } if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) - { - bool complete = true; - foreach (var childId in context.ExecutionPointer.Children) - { - complete = complete && IsBranchComplete(context.Workflow.ExecutionPointers, childId); - } - - if (complete) + { + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) { return ExecutionResult.Next(); } diff --git a/src/WorkflowCore/Primitives/While.cs b/src/WorkflowCore/Primitives/While.cs index 4565e7c48..154d7cf87 100644 --- a/src/WorkflowCore/Primitives/While.cs +++ b/src/WorkflowCore/Primitives/While.cs @@ -23,11 +23,8 @@ public override ExecutionResult Run(IStepExecutionContext context) if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) { - for (int i = context.ExecutionPointer.Children.Count - 1; i > -1; i--) - { - if (!IsBranchComplete(context.Workflow.ExecutionPointers, context.ExecutionPointer.Children[i])) - return ExecutionResult.Persist(context.PersistenceData); - } + if (!context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) + return ExecutionResult.Persist(context.PersistenceData); return ExecutionResult.Persist(null); //re-evaluate condition on next pass } diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index a84e0bba0..8b3a77058 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -45,6 +45,7 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(); services.AddSingleton(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/WorkflowCore/Services/CancellationProcessor.cs b/src/WorkflowCore/Services/CancellationProcessor.cs new file mode 100644 index 000000000..d32d43f1f --- /dev/null +++ b/src/WorkflowCore/Services/CancellationProcessor.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Services +{ + public class CancellationProcessor : ICancellationProcessor + { + protected readonly ILogger _logger; + private readonly IExecutionResultProcessor _executionResultProcessor; + + public CancellationProcessor(IExecutionResultProcessor executionResultProcessor, ILoggerFactory logFactory) + { + _executionResultProcessor = executionResultProcessor; + _logger = logFactory.CreateLogger(); + } + + public void ProcessCancellations(WorkflowInstance workflow, WorkflowDefinition workflowDef, WorkflowExecutorResult executionResult) + { + foreach (var step in workflowDef.Steps.Where(x => x.CancelCondition != null)) + { + var func = step.CancelCondition.Compile(); + var cancel = false; + try + { + cancel = (bool)(func.DynamicInvoke(workflow.Data)); + } + catch (Exception ex) + { + _logger.LogError(default(EventId), ex.Message, ex); + } + if (cancel) + { + var toCancel = workflow.ExecutionPointers.Where(x => x.StepId == step.Id && x.Status != PointerStatus.Complete && x.Status != PointerStatus.Cancelled).ToList(); + + foreach (var ptr in toCancel) + { + ptr.EndTime = DateTime.Now.ToUniversalTime(); + ptr.Active = false; + ptr.Status = PointerStatus.Cancelled; + + if (step.ProceedOnCancel) + { + _executionResultProcessor.ProcessExecutionResult(workflow, workflowDef, ptr, step, ExecutionResult.Next(), executionResult); + } + + foreach (var descendent in workflow.ExecutionPointers.FindByScope(ptr.Id).Where(x => x.Status != PointerStatus.Complete && x.Status != PointerStatus.Cancelled)) + { + descendent.EndTime = DateTime.Now.ToUniversalTime(); + descendent.Active = false; + descendent.Status = PointerStatus.Cancelled; + } + } + } + } + } + } +} diff --git a/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs b/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs index 4074b9942..40a826b91 100644 --- a/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs +++ b/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs @@ -41,5 +41,16 @@ public void Subscribe(Action action) { _subscribers.Add(action); } + + public Task Start() + { + return Task.CompletedTask; + } + + public Task Stop() + { + _subscribers.Clear(); + return Task.CompletedTask; + } } } diff --git a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs index 4c7af8831..8f4084ba3 100644 --- a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs +++ b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs @@ -69,19 +69,18 @@ private List ConvertSteps(ICollection source, Type d var containerType = typeof(WorkflowStep<>).MakeGenericType(stepType); var targetStep = (containerType.GetConstructor(new Type[] { }).Invoke(null) as WorkflowStep); + if (nextStep.Saga) + { + containerType = typeof(SagaContainer<>).MakeGenericType(stepType); + targetStep = (containerType.GetConstructor(new Type[] { }).Invoke(null) as WorkflowStep); + } + if (!string.IsNullOrEmpty(nextStep.CancelCondition)) { - containerType = typeof(CancellableStep<,>).MakeGenericType(stepType, dataType); var cancelExprType = typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(dataType, typeof(bool))); var dataParameter = Expression.Parameter(dataType, "data"); var cancelExpr = DynamicExpressionParser.ParseLambda(new[] { dataParameter }, typeof(bool), nextStep.CancelCondition); - targetStep = (containerType.GetConstructor(new Type[] { cancelExprType }).Invoke(new[] { cancelExpr }) as WorkflowStep); - } - - if (nextStep.Saga) //TODO: cancellable saga??? - { - containerType = typeof(SagaContainer<>).MakeGenericType(stepType); - targetStep = (containerType.GetConstructor(new Type[] { }).Invoke(null) as WorkflowStep); + targetStep.CancelCondition = cancelExpr; } targetStep.Id = i; diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index 0f630ea73..de3e98c01 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -37,7 +37,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP while (scope.Any()) { var pointerId = scope.Pop(); - var scopePointer = workflow.ExecutionPointers.First(x => x.Id == pointerId); + var scopePointer = workflow.ExecutionPointers.FindById(pointerId); var scopeStep = def.Steps.First(x => x.Id == scopePointer.StepId); var resume = true; @@ -46,7 +46,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP if (scope.Any()) { var parentId = scope.Peek(); - var parentPointer = workflow.ExecutionPointers.First(x => x.Id == parentId); + var parentPointer = workflow.ExecutionPointers.FindById(parentId); var parentStep = def.Steps.First(x => x.Id == parentPointer.StepId); resume = parentStep.ResumeChildrenAfterCompensation; revert = parentStep.RevertChildrenAfterCompensation; diff --git a/src/WorkflowCore/Services/ExecutionPointerFactory.cs b/src/WorkflowCore/Services/ExecutionPointerFactory.cs index c98739736..927e06e4e 100644 --- a/src/WorkflowCore/Services/ExecutionPointerFactory.cs +++ b/src/WorkflowCore/Services/ExecutionPointerFactory.cs @@ -34,7 +34,7 @@ public ExecutionPointer BuildNextPointer(WorkflowDefinition def, ExecutionPointe ContextItem = pointer.ContextItem, Status = PointerStatus.Pending, StepName = def.Steps.First(x => x.Id == outcomeTarget.NextStep).Name, - Scope = new Stack(pointer.Scope) + Scope = new List(pointer.Scope) }; } @@ -54,7 +54,7 @@ public ExecutionPointer BuildChildPointer(WorkflowDefinition def, ExecutionPoint ContextItem = branch, Status = PointerStatus.Pending, StepName = def.Steps.First(x => x.Id == childDefinitionId).Name, - Scope = childScope + Scope = new List(childScope) }; } @@ -70,7 +70,7 @@ public ExecutionPointer BuildCompensationPointer(WorkflowDefinition def, Executi ContextItem = pointer.ContextItem, Status = PointerStatus.Pending, StepName = def.Steps.First(x => x.Id == compensationStepId).Name, - Scope = new Stack(pointer.Scope) + Scope = new List(pointer.Scope) }; } diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index 13b30afb8..9c8bad6d9 100755 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -128,7 +128,7 @@ public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition de while (scope.Count > 0) { var pointerId = scope.Pop(); - var pointer = workflow.ExecutionPointers.First(x => x.Id == pointerId); + var pointer = workflow.ExecutionPointers.FindById(pointerId); var step = def.Steps.First(x => x.Id == pointer.StepId); if (step.CompensationStepId.HasValue) return step.CompensationStepId.Value; diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index 25cd3b6c7..f648a418f 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -111,12 +111,8 @@ public IStepBuilder Output(Expression WaitFor(string eventName, Expression> eventKey, Expression> effectiveDate = null, Expression> cancelCondition = null) { - WorkflowStep newStep; - - if (cancelCondition != null) - newStep = new CancellableStep(cancelCondition); - else - newStep = new WorkflowStep(); + var newStep = new WorkflowStep(); + newStep.CancelCondition = cancelCondition; WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); @@ -134,12 +130,8 @@ public IStepBuilder WaitFor(string eventName, Expression WaitFor(string eventName, Expression> eventKey, Expression> effectiveDate = null, Expression> cancelCondition = null) { - WorkflowStep newStep; - - if (cancelCondition != null) - newStep = new CancellableStep(cancelCondition); - else - newStep = new WorkflowStep(); + var newStep = new WorkflowStep(); + newStep.CancelCondition = cancelCondition; WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); @@ -379,7 +371,9 @@ public IContainerStepBuilder Schedule(Expression Recur(Expression> interval, Expression> until) { - var newStep = new CancellableStep(until); + var newStep = new WorkflowStep(); + newStep.CancelCondition = until; + Expression> intervalExpr = (x => x.Interval); Expression> untilExpr = (x => x.StopCondition); newStep.Inputs.Add(new DataMapping() { Source = interval, Target = intervalExpr }); @@ -448,5 +442,12 @@ public IStepBuilder CompensateWithSequence(Action CancelCondition(Expression> cancelCondition, bool proceedAfterCancel = false) + { + Step.CancelCondition = cancelCondition; + Step.ProceedOnCancel = proceedAfterCancel; + return this; + } } } diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 5702c657e..708ae747f 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -72,7 +72,10 @@ public async Task StartWorkflow(string workflowId, int? version, if ((def.DataType != null) && (data == null)) { - wf.Data = new TData(); + if (typeof(TData) == def.DataType) + wf.Data = new TData(); + else + wf.Data = def.DataType.GetConstructor(new Type[0]).Invoke(new object[0]); } wf.ExecutionPointers.Add(_pointerFactory.BuildGenesisPointer(def)); diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 0dd00f45b..c2b643aee 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -20,17 +20,19 @@ public class WorkflowExecutor : IWorkflowExecutor protected readonly IDateTimeProvider _datetimeProvider; protected readonly ILogger _logger; private readonly IExecutionResultProcessor _executionResultProcessor; + private readonly ICancellationProcessor _cancellationProcessor; private readonly ILifeCycleEventPublisher _publisher; private readonly WorkflowOptions _options; private IWorkflowHost Host => _serviceProvider.GetService(); - public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, WorkflowOptions options, ILoggerFactory loggerFactory) + public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, ICancellationProcessor cancellationProcessor, WorkflowOptions options, ILoggerFactory loggerFactory) { _serviceProvider = serviceProvider; _registry = registry; _datetimeProvider = datetimeProvider; _publisher = publisher; + _cancellationProcessor = cancellationProcessor; _options = options; _logger = loggerFactory.CreateLogger(); _executionResultProcessor = executionResultProcessor; @@ -50,6 +52,9 @@ public async Task Execute(WorkflowInstance workflow) foreach (var pointer in exePointers) { + if (pointer.Status == PointerStatus.Cancelled) + continue; + var step = def.Steps.First(x => x.Id == pointer.StepId); if (step != null) { @@ -148,6 +153,7 @@ public async Task Execute(WorkflowInstance workflow) _executionResultProcessor.HandleStepException(workflow, def, pointer, step, ex); Host.ReportStepError(workflow, step, ex); } + _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); } else { @@ -256,7 +262,7 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) { foreach (var pointer in workflow.ExecutionPointers.Where(x => x.Active && (x.Children ?? new List()).Count > 0)) { - if (!workflow.ExecutionPointers.Where(x => x.Scope.Contains(pointer.Id)).All(x => x.EndTime.HasValue)) + if (!workflow.ExecutionPointers.FindByScope(pointer.Id).All(x => x.EndTime.HasValue)) continue; if (!pointer.SleepUntil.HasValue) diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index 6a4c91d22..85bf079de 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -31,6 +31,8 @@ public class WorkflowHost : IWorkflowHost, IDisposable public IQueueProvider QueueProvider { get; private set; } public ILogger Logger { get; private set; } + private readonly ILifeCycleEventHub _lifeCycleEventHub; + public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, WorkflowOptions options, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IEnumerable backgroundTasks, IWorkflowController workflowController, ILifeCycleEventHub lifeCycleEventHub) { PersistenceStore = persistenceStore; @@ -42,7 +44,8 @@ public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueP LockProvider = lockProvider; _backgroundTasks = backgroundTasks; _workflowController = workflowController; - lifeCycleEventHub.Subscribe(HandleLifeCycleEvent); + _lifeCycleEventHub = lifeCycleEventHub; + _lifeCycleEventHub.Subscribe(HandleLifeCycleEvent); } public Task StartWorkflow(string workflowId, object data = null, string reference=null) @@ -78,7 +81,8 @@ public void Start() PersistenceStore.EnsureStoreExists(); QueueProvider.Start().Wait(); LockProvider.Start().Wait(); - + _lifeCycleEventHub.Start().Wait(); + Logger.LogInformation("Starting backgroud tasks"); foreach (var task in _backgroundTasks) @@ -95,8 +99,9 @@ public void Stop() Logger.LogInformation("Worker tasks stopped"); - QueueProvider.Stop(); - LockProvider.Stop(); + QueueProvider.Stop().Wait(); + LockProvider.Stop().Wait(); + _lifeCycleEventHub.Stop().Wait(); } public void RegisterWorkflow() diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 79ac162f4..d02b350f2 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.6.11 - 1.6.11.0 - 1.6.11.0 + 1.7.0 + 1.7.0.0 + 1.7.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png @@ -26,7 +26,7 @@ - + diff --git a/src/extensions/WorkflowCore.Users/Primitives/Escalate.cs b/src/extensions/WorkflowCore.Users/Primitives/Escalate.cs index 67603556f..bdd8c6518 100644 --- a/src/extensions/WorkflowCore.Users/Primitives/Escalate.cs +++ b/src/extensions/WorkflowCore.Users/Primitives/Escalate.cs @@ -16,7 +16,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (context.PersistenceData != null) { - var taskStep = context.Workflow.ExecutionPointers.Find(x => x.Id == context.ExecutionPointer.PredecessorId); + var taskStep = context.Workflow.ExecutionPointers.FindById(context.ExecutionPointer.PredecessorId); if (!taskStep.EventPublished) { diff --git a/src/extensions/WorkflowCore.Users/Primitives/EscalateStep.cs b/src/extensions/WorkflowCore.Users/Primitives/EscalateStep.cs index f05194c9e..7ed21b795 100644 --- a/src/extensions/WorkflowCore.Users/Primitives/EscalateStep.cs +++ b/src/extensions/WorkflowCore.Users/Primitives/EscalateStep.cs @@ -13,7 +13,7 @@ public class EscalateStep : WorkflowStep public override void AfterWorkflowIteration(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { base.AfterWorkflowIteration(executorResult, defintion, workflow, executionPointer); - var taskStep = workflow.ExecutionPointers.Find(x => x.Id == executionPointer.PredecessorId); + var taskStep = workflow.ExecutionPointers.FindById(executionPointer.PredecessorId); if (taskStep.EventPublished) { diff --git a/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs b/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs index ee757cd6d..536e56d84 100644 --- a/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs +++ b/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs @@ -57,11 +57,7 @@ public override ExecutionResult Run(IStepExecutionContext context) if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) { - bool complete = true; - foreach (var childId in context.ExecutionPointer.Children) - complete = complete && IsBranchComplete(context.Workflow.ExecutionPointers, childId); - - if (complete) + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) return ExecutionResult.Next(); else { diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 909e6fcfe..15f1cb68d 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -18,9 +18,9 @@ false false Provides extensions for Workflow Core to enable human workflows. - 1.6.2 - 1.6.2.0 - 1.6.2.0 + 1.7.0 + 1.7.0.0 + 1.7.0.0 diff --git a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj index baeef65f8..74b5f8181 100644 --- a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj +++ b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj @@ -17,7 +17,7 @@ false false false - 1.6.0 + 1.7.0 WebAPI wrapper for Workflow host diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj b/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj index 902b41e51..dc7c6c292 100644 --- a/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj +++ b/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj @@ -17,11 +17,11 @@ false false false - 1.6.0 + 1.7.0 Distributed lock provider for Workflow-core using Redis - 1.6.0.0 - 1.6.0.0 + 1.7.0.0 + 1.7.0.0 diff --git a/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj b/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj index d6c4ab17f..13a9d87ab 100644 --- a/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj @@ -5,7 +5,7 @@ Distributed lock provider for Workflow-core using SQL Server https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md - 1.4.0 + 1.7.0 diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs index 56420b344..f7173ce75 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs @@ -27,18 +27,19 @@ internal static PersistedWorkflow ToPersistable(this WorkflowInstance instance, persistable.Status = instance.Status; persistable.CreateTime = instance.CreateTime; persistable.CompleteTime = instance.CompleteTime; + persistable.ExecutionPointers = new PersistedExecutionPointerCollection(instance.ExecutionPointers.Count); foreach (var ep in instance.ExecutionPointers) { - var persistedEP = persistable.ExecutionPointers.FirstOrDefault(x => x.Id == ep.Id); + var persistedEP = persistable.ExecutionPointers.FindById(ep.Id); if (persistedEP == null) { persistedEP = new PersistedExecutionPointer(); + persistedEP.Id = ep.Id ?? Guid.NewGuid().ToString(); persistable.ExecutionPointers.Add(persistedEP); - } - - persistedEP.Id = ep.Id ?? Guid.NewGuid().ToString(); + } + persistedEP.StepId = ep.StepId; persistedEP.Active = ep.Active; persistedEP.SleepUntil = ep.SleepUntil; @@ -134,10 +135,11 @@ internal static WorkflowInstance ToWorkflowInstance(this PersistedWorkflow insta if (instance.CompleteTime.HasValue) result.CompleteTime = DateTime.SpecifyKind(instance.CompleteTime.Value, DateTimeKind.Utc); + result.ExecutionPointers = new ExecutionPointerCollection(instance.ExecutionPointers.Count + 8); + foreach (var ep in instance.ExecutionPointers) { - var pointer = new ExecutionPointer(); - result.ExecutionPointers.Add(pointer); + var pointer = new ExecutionPointer(); pointer.Id = ep.Id; pointer.StepId = ep.StepId; @@ -171,12 +173,14 @@ internal static WorkflowInstance ToWorkflowInstance(this PersistedWorkflow insta pointer.Status = ep.Status; if (!string.IsNullOrEmpty(ep.Scope)) - pointer.Scope = new Stack(ep.Scope.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); + pointer.Scope = new List(ep.Scope.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); foreach (var attr in ep.ExtensionAttributes) { pointer.ExtensionAttributes[attr.AttributeKey] = JsonConvert.DeserializeObject(attr.AttributeValue, SerializerSettings); } + + result.ExecutionPointers.Add(pointer); } return result; diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointerCollection.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointerCollection.cs new file mode 100644 index 000000000..c8950803b --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointerCollection.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Persistence.EntityFramework.Models +{ + public class PersistedExecutionPointerCollection : ICollection + { + private readonly Dictionary _dictionary; + + public PersistedExecutionPointerCollection() + { + _dictionary = new Dictionary(); + } + + public PersistedExecutionPointerCollection(int capacity) + { + _dictionary = new Dictionary(capacity); + } + + public IEnumerator GetEnumerator() + { + return _dictionary.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public PersistedExecutionPointer FindById(string id) + { + if (!_dictionary.ContainsKey(id)) + return null; + + return _dictionary[id]; + } + + public void Add(PersistedExecutionPointer item) + { + _dictionary.Add(item.Id, item); + } + + public void Clear() + { + _dictionary.Clear(); + } + + public bool Contains(PersistedExecutionPointer item) + { + return _dictionary.ContainsValue(item); + } + + public void CopyTo(PersistedExecutionPointer[] array, int arrayIndex) + { + _dictionary.Values.CopyTo(array, arrayIndex); + } + + public bool Remove(PersistedExecutionPointer item) + { + return _dictionary.Remove(item.Id); + } + + public int Count => _dictionary.Count; + public bool IsReadOnly => false; + } +} diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedWorkflow.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedWorkflow.cs index 626e2661f..b212bc0c9 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedWorkflow.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedWorkflow.cs @@ -27,9 +27,8 @@ public class PersistedWorkflow [MaxLength(200)] public string Reference { get; set; } - public virtual List ExecutionPointers { get; set; } = new List(); + public virtual PersistedExecutionPointerCollection ExecutionPointers { get; set; } = new PersistedExecutionPointerCollection(); - //[Index] public long? NextExecution { get; set; } public string Data { get; set; } diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 425db8d05..321fe4915 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -15,10 +15,10 @@ false false false - 1.8.0 + 1.8.1 Base package for Workflow-core peristence providers using entity framework - 1.8.0.0 - 1.8.0.0 + 1.8.1.0 + 1.8.1.0 @@ -28,7 +28,7 @@ - + diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index ef95c540a..4da3c1b7b 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -4,7 +4,7 @@ Workflow Core MongoDB Persistence Provider 1.1.0 Daniel Gerlag - netstandard1.3 + netstandard1.5 WorkflowCore.Persistence.MongoDB WorkflowCore.Persistence.MongoDB workflow;.NET;Core;state machine;WorkflowCore;MongoDB;Mongo @@ -17,10 +17,10 @@ false false false - 1.6.4 + 1.7.0 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 1.6.4.0 - 1.6.4.0 + 1.7.0.0 + 1.7.0.0 @@ -28,8 +28,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index b1449ac79..989df4ceb 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a MySQL database. - 1.0.0 - 1.0.0.0 - 1.0.0.0 + 1.1.0 + 1.1.0.0 + 1.1.0.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 2a4ff7080..885d4611a 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 1.8.0 - 1.8.0.0 - 1.8.0.0 + 1.8.1 + 1.8.1.0 + 1.8.1.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 7d8e7b9bb..3535ef304 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,10 +15,10 @@ false false false - 1.8.0 + 1.8.1 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 1.8.0.0 - 1.8.0.0 + 1.8.1.0 + 1.8.1.0 diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 22e30b0a9..128caa18f 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 1.7.0 - 1.7.0.0 - 1.7.0.0 + 1.7.1 + 1.7.1.0 + 1.7.1.0 diff --git a/src/providers/WorkflowCore.Providers.AWS/README.md b/src/providers/WorkflowCore.Providers.AWS/README.md index a729b3828..77ea0e7b1 100644 --- a/src/providers/WorkflowCore.Providers.AWS/README.md +++ b/src/providers/WorkflowCore.Providers.AWS/README.md @@ -24,9 +24,9 @@ Use the `IServiceCollection` extension methods when building your service provid ```C# services.AddWorkflow(cfg => { - cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "table-prefix"); + cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "table-prefix"); cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); - cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); + cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); }); ``` diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs index 489235133..18bde2c61 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -22,6 +22,7 @@ public class DynamoLockProvider : IDistributedLockProvider private readonly List _localLocks; private Task _heartbeatTask; private CancellationTokenSource _cancellationTokenSource; + private readonly AutoResetEvent _mutex = new AutoResetEvent(true); public DynamoLockProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, string tableName, ILoggerFactory logFactory) { @@ -76,7 +77,18 @@ public async Task AcquireLock(string Id, CancellationToken cancellationTok public async Task ReleaseLock(string Id) { - _localLocks.Remove(Id); + if (_mutex.WaitOne()) + { + try + { + _localLocks.Remove(Id); + } + finally + { + _mutex.Set(); + } + } + try { var req = new DeleteItemRequest() @@ -129,29 +141,46 @@ private async void SendHeartbeat() try { await Task.Delay(_heartbeat, _cancellationTokenSource.Token); - foreach (var item in _localLocks) + if (_mutex.WaitOne()) { - var req = new PutItemRequest + try { - TableName = _tableName, - Item = new Dictionary - { - { "id", new AttributeValue(item) }, - { "lock_owner", new AttributeValue(_nodeId) }, - { "expires", new AttributeValue() + foreach (var item in _localLocks) + { + var req = new PutItemRequest { - N = Convert.ToString(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + _ttl) + TableName = _tableName, + Item = new Dictionary + { + { "id", new AttributeValue(item) }, + { "lock_owner", new AttributeValue(_nodeId) }, + { "expires", new AttributeValue() + { + N = Convert.ToString(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + _ttl) + } + } + }, + ConditionExpression = "lock_owner = :nodeId", + ExpressionAttributeValues = new Dictionary + { + { ":nodeId", new AttributeValue(_nodeId) } + } + }; + + try + { + await _client.PutItemAsync(req, _cancellationTokenSource.Token); + } + catch (ConditionalCheckFailedException) + { + _logger.LogWarning($"Lock not owned anymore when sending heartbeat for {item}"); } } - }, - ConditionExpression = "lock_owner = :nodeId", - ExpressionAttributeValues = new Dictionary + } + finally { - { ":nodeId", new AttributeValue(_nodeId) } + _mutex.Set(); } - }; - - await _client.PutItemAsync(req, _cancellationTokenSource.Token); } } catch (Exception ex) diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index 28e354509..d63fb07c9 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -292,11 +292,7 @@ public async Task MarkEventProcessed(string id) { { "id", new AttributeValue(id) } }, - UpdateExpression = "REMOVE not_processed", - //ExpressionAttributeValues = new Dictionary() - //{ - // { ":processed" , new AttributeValue(true.ToString()) } - //} + UpdateExpression = "REMOVE not_processed" }; await _client.UpdateItemAsync(request); } diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 5e453fc6d..abcc32771 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,15 +11,15 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 1.6.11 - 1.6.11.0 + 1.7.0 + 1.7.0.0 - + - + diff --git a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj index a31afecd3..f07725052 100644 --- a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj +++ b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj @@ -7,19 +7,19 @@ - Provides distributed lock management on Workflow Core - Provides Queueing support on Workflow Core workflow workflowcore dlm - 1.3.0 + 1.7.0 $(PackageTargetFallback);dnxcore50 https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git Daniel Gerlag - 1.3.0.0 - 1.3.0.0 + 1.7.0.0 + 1.7.0.0 - + diff --git a/src/providers/WorkflowCore.Providers.Redis/README.md b/src/providers/WorkflowCore.Providers.Redis/README.md new file mode 100644 index 000000000..f25e50642 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Redis/README.md @@ -0,0 +1,37 @@ +# Redis providers for Workflow Core + +* Provides Queueing support on [Workflow Core](../../README.md) backed by Redis. +* Provides Distributed locking support on [Workflow Core](../../README.md) backed by Redis. +* Provides event hub support on [Workflow Core](../../README.md) backed by Redis. + +This makes it possible to have a cluster of nodes processing your workflows. + +## Installing + +Install the NuGet package "WorkflowCore.Providers.Redis" + +Using Nuget package console + ``` + PM> Install-Package WorkflowCore.Providers.Redis + ``` +Using .NET CLI + ``` + dotnet add package WorkflowCore.Providers.Redis + ``` + + +## Usage + +Use the `IServiceCollection` extension methods when building your service provider +* .UseRedisQueues +* .UseRedisLocking +* .UseRedisEventHub + +```C# +services.AddWorkflow(cfg => +{ + cfg.UseRedisLocking("localhost:6379"); + cfg.UseRedisQueues("localhost:6379", "my-app"); + cfg.UseRedisEventHub("localhost:6379", "my-channel") +}); +``` diff --git a/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..1ce0a091d --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.Extensions.Logging; +using StackExchange.Redis; +using WorkflowCore.Models; +using WorkflowCore.Providers.Redis.Services; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + public static WorkflowOptions UseRedisQueues(this WorkflowOptions options, string connectionString, string prefix) + { + options.UseQueueProvider(sp => new RedisQueueProvider(connectionString, prefix, sp.GetService())); + return options; + } + + public static WorkflowOptions UseRedisLocking(this WorkflowOptions options, string connectionString) + { + options.UseDistributedLockManager(sp => new RedisLockProvider(connectionString, sp.GetService())); + return options; + } + + public static WorkflowOptions UseRedisEventHub(this WorkflowOptions options, string connectionString, string channel) + { + options.UseEventHub(sp => new RedisLifeCycleEventHub(connectionString, channel, sp.GetService())); + return options; + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs new file mode 100644 index 000000000..8d7d1e35d --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using StackExchange.Redis; +using WorkflowCore.Interface; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Providers.Redis.Services +{ + public class RedisLifeCycleEventHub : ILifeCycleEventHub + { + private readonly ILogger _logger; + private readonly string _connectionString; + private readonly string _channel; + private ICollection> _subscribers = new HashSet>(); + private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + private IConnectionMultiplexer _multiplexer; + private ISubscriber _subscriber; + + public RedisLifeCycleEventHub(string connectionString, string channel, ILoggerFactory logFactory) + { + _connectionString = connectionString; + _channel = channel; + _logger = logFactory.CreateLogger(GetType()); + } + + public async Task PublishNotification(LifeCycleEvent evt) + { + if (_subscriber == null) + throw new InvalidOperationException(); + + var data = JsonConvert.SerializeObject(evt, _serializerSettings); + await _subscriber.PublishAsync(_channel, data); + } + + public void Subscribe(Action action) + { + _subscribers.Add(action); + } + + public async Task Start() + { + _multiplexer = await ConnectionMultiplexer.ConnectAsync(_connectionString); + _subscriber = _multiplexer.GetSubscriber(); + _subscriber.Subscribe(_channel, (channel, message) => { + var evt = JsonConvert.DeserializeObject(message, _serializerSettings); + NotifySubscribers((LifeCycleEvent)evt); + }); + } + + public async Task Stop() + { + await _subscriber.UnsubscribeAllAsync(); + await _multiplexer.CloseAsync(); + _subscriber = null; + _multiplexer = null; + } + + private void NotifySubscribers(LifeCycleEvent evt) + { + foreach (var subscriber in _subscribers) + { + try + { + subscriber(evt); + } + catch (Exception ex) + { + _logger.LogWarning(default(EventId), ex, $"Error on event subscriber: {ex.Message}"); + } + } + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs new file mode 100644 index 000000000..1b18e7539 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RedLockNet; +using RedLockNet.SERedis; +using RedLockNet.SERedis.Configuration; +using StackExchange.Redis; +using WorkflowCore.Interface; + +namespace WorkflowCore.Providers.Redis.Services +{ + public class RedisLockProvider : IDistributedLockProvider + { + private readonly ILogger _logger; + private readonly string _connectionString; + private IConnectionMultiplexer _multiplexer; + private RedLockFactory _redlockFactory; + private readonly TimeSpan _lockTimeout = TimeSpan.FromMinutes(1); + private readonly List ManagedLocks = new List(); + + public RedisLockProvider(string connectionString, ILoggerFactory logFactory) + { + _connectionString = connectionString; + _logger = logFactory.CreateLogger(GetType()); + } + + public async Task AcquireLock(string Id, CancellationToken cancellationToken) + { + if (_redlockFactory == null) + throw new InvalidOperationException(); + + var redLock = await _redlockFactory.CreateLockAsync(Id, _lockTimeout); + + if (redLock.IsAcquired) + { + lock (ManagedLocks) + { + ManagedLocks.Add(redLock); + } + return true; + } + + return false; + } + + public Task ReleaseLock(string Id) + { + if (_redlockFactory == null) + throw new InvalidOperationException(); + + lock (ManagedLocks) + { + foreach (var redLock in ManagedLocks) + { + if (redLock.Resource == Id) + { + redLock.Dispose(); + ManagedLocks.Remove(redLock); + break; + } + } + } + + return Task.CompletedTask; + } + + public async Task Start() + { + _multiplexer = await ConnectionMultiplexer.ConnectAsync(_connectionString); + _redlockFactory = RedLockFactory.Create(new List() { new RedLockMultiplexer(_multiplexer) }); + } + + public async Task Stop() + { + _redlockFactory?.Dispose(); + await _multiplexer.CloseAsync(); + _multiplexer = null; + + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs new file mode 100644 index 000000000..fdfb50240 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using StackExchange.Redis; +using WorkflowCore.Interface; + +namespace WorkflowCore.Providers.Redis.Services +{ + public class RedisQueueProvider : IQueueProvider + { + private readonly ILogger _logger; + private readonly string _connectionString; + private readonly string _prefix; + private const string WORKFLOW_QUEUE = "workflows"; + private const string EVENT_QUEUE = "events"; + private IConnectionMultiplexer _multiplexer; + private IDatabase _redis; + + public RedisQueueProvider(string connectionString, string prefix, ILoggerFactory logFactory) + { + _connectionString = connectionString; + _prefix = prefix; + _logger = logFactory.CreateLogger(GetType()); + } + + public async Task QueueWork(string id, QueueType queue) + { + if (_redis == null) + throw new InvalidOperationException(); + + await _redis.ListRightPushAsync(GetQueueName(queue), id, When.Always); + } + + public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) + { + if (_redis == null) + throw new InvalidOperationException(); + + var result = await _redis.ListLeftPopAsync(GetQueueName(queue)); + + if (result.IsNull) + return null; + + return result; + } + + public bool IsDequeueBlocking => false; + + public async Task Start() + { + _multiplexer = await ConnectionMultiplexer.ConnectAsync(_connectionString); + _redis = _multiplexer.GetDatabase(); + } + + public async Task Stop() + { + await _multiplexer.CloseAsync(); + _redis = null; + _multiplexer = null; + } + + public void Dispose() + { + } + + private string GetQueueName(QueueType queue) + { + var queueName = string.Empty; + switch (queue) + { + case QueueType.Workflow: + queueName = $"{_prefix}-{WORKFLOW_QUEUE}"; + break; + case QueueType.Event: + queueName = $"{_prefix}-{EVENT_QUEUE}"; + break; + } + + return queueName; + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj new file mode 100644 index 000000000..9f410a435 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -0,0 +1,27 @@ + + + + netstandard2.0 + 1.7.0 + https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md + https://github.com/danielgerlag/workflow-core.git + git + https://github.com/danielgerlag/workflow-core + Redis providers for Workflow Core + +- Provides Queueing support on Workflow Core +- Provides distributed locking support on Workflow Core + + + + + + + + + + + + + + diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj index 5c75e91fb..5d13307c4 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj @@ -17,10 +17,10 @@ false false false - 1.3.0 + 1.7.0 Queue provider for Workflow-core using RabbitMQ - 1.3.0.0 - 1.3.0.0 + 1.7.0.0 + 1.7.0.0 @@ -28,7 +28,7 @@ - + diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 87a6bef09..6ef3bf079 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -5,7 +5,7 @@ Roberto Paterlini Queue provider for Workflow-core using SQL Server Service Broker - 1.0.0-alpha + 1.0.1-alpha diff --git a/src/samples/WorkflowCore.Sample04/Program.cs b/src/samples/WorkflowCore.Sample04/Program.cs index 7eabd55ec..ca8081d11 100644 --- a/src/samples/WorkflowCore.Sample04/Program.cs +++ b/src/samples/WorkflowCore.Sample04/Program.cs @@ -70,7 +70,12 @@ private static IServiceProvider ConfigureServices() // cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); //}); - //services.AddWorkflow(x => x.UseRedlock(new System.Net.DnsEndPoint("127.0.0.1", 32768))); + //services.AddWorkflow(cfg => + //{ + // cfg.UseRedisLocking("localhost:6379"); + // cfg.UseRedisQueues("localhost:6379", "sample4"); + // cfg.UseRedisEventHub("localhost:6379", "channel1"); + //}); //services.AddWorkflow(x => //{ diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index e789fa52e..caf8f9550 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -17,8 +17,8 @@ + - diff --git a/src/samples/WorkflowCore.Sample13/ParallelWorkflow.cs b/src/samples/WorkflowCore.Sample13/ParallelWorkflow.cs index d885f9f55..8b4ea0d0a 100644 --- a/src/samples/WorkflowCore.Sample13/ParallelWorkflow.cs +++ b/src/samples/WorkflowCore.Sample13/ParallelWorkflow.cs @@ -25,7 +25,9 @@ public void Build(IWorkflowBuilder builder) then.StartWith() .Input(step => step.Message, data => "Item 2.1") .Then() - .Input(step => step.Message, data => "Item 2.2")) + .Input(step => step.Message, data => "Item 2.2") + .Then() + .Input(step => step.Message, data => "Item 2.3")) .Do(then => then.StartWith() .Input(step => step.Message, data => "Item 3.1") diff --git a/src/samples/WorkflowCore.Sample13/Program.cs b/src/samples/WorkflowCore.Sample13/Program.cs index f3364949b..8240a09cb 100644 --- a/src/samples/WorkflowCore.Sample13/Program.cs +++ b/src/samples/WorkflowCore.Sample13/Program.cs @@ -23,7 +23,7 @@ public static void Main(string[] args) host.Start(); Console.WriteLine("Starting workflow..."); - controller.StartWorkflow("parallel-sample"); + controller.StartWorkflow("parallel-sample"); Console.ReadLine(); host.Stop(); diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 94f5d773f..09dc7c647 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -18,7 +18,7 @@ - + diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index 96c69f2de..eb4ea7f20 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -32,7 +32,7 @@ - + diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 2a402b926..63c11078d 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 1.6.0 - 1.6.0.0 - 1.6.0.0 + 1.7.0 + 1.7.0.0 + 1.7.0.0 Facilitates testing of workflows built on Workflow-Core diff --git a/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs b/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs rename to test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs diff --git a/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs rename to test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs rename to test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs rename to test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs rename to test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs rename to test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs rename to test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs rename to test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs rename to test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs rename to test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs rename to test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs diff --git a/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs similarity index 100% rename from WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs rename to test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs diff --git a/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj similarity index 55% rename from WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj rename to test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index 0a733560f..d72f927a8 100644 --- a/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -7,17 +7,17 @@ - + - - - - + + + + diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index efa0455be..bc41b01d9 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -27,7 +27,7 @@ - + diff --git a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs index 97cdd0fa1..14847b1f1 100644 --- a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs +++ b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs @@ -54,16 +54,19 @@ public void GetWorkflowInstance_should_retrieve_workflow() }; workflow.ExecutionPointers.Add(new ExecutionPointer() { - Id = Guid.NewGuid().ToString(), + Id = "1", Active = true, StepId = 0, - SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime() + SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime(), + Scope = new List() { "4", "3", "2", "1" } }); var workflowId = Subject.CreateNewWorkflow(workflow).Result; var retrievedWorkflow = Subject.GetWorkflowInstance(workflowId).Result; retrievedWorkflow.ShouldBeEquivalentTo(workflow); + retrievedWorkflow.ExecutionPointers.FindById("1") + .Scope.Should().ContainInOrder(workflow.ExecutionPointers.FindById("1").Scope); } [Fact] @@ -84,7 +87,8 @@ public void PersistWorkflow() { Id = Guid.NewGuid().ToString(), Active = true, - StepId = 0 + StepId = 0, + Scope = new List() { "1", "2", "3", "4" } }); var workflowId = Subject.CreateNewWorkflow(oldWorkflow).Result; var newWorkflow = Utils.DeepCopy(oldWorkflow); diff --git a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs index 18ab2427c..dbaa90e53 100644 --- a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs @@ -47,8 +47,8 @@ public void should_advance_workflow() { //arrange var definition = new WorkflowDefinition(); - var pointer1 = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; - var pointer2 = new ExecutionPointer(); + var pointer1 = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer2 = new ExecutionPointer() { Id = "2" }; var outcome = new StepOutcome() { NextStep = 1 }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); @@ -76,7 +76,7 @@ public void should_set_persistence_data() //arrange var persistenceData = new object(); var definition = new WorkflowDefinition(); - var pointer = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); @@ -94,7 +94,7 @@ public void should_subscribe_to_event() { //arrange var definition = new WorkflowDefinition(); - var pointer = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); @@ -116,9 +116,9 @@ public void should_select_correct_outcomes() { //arrange var definition = new WorkflowDefinition(); - var pointer1 = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; - var pointer2 = new ExecutionPointer(); - var pointer3 = new ExecutionPointer(); + var pointer1 = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer2 = new ExecutionPointer() { Id = "2" }; + var pointer3 = new ExecutionPointer() { Id = "3" }; var outcome1 = new StepOutcome() { NextStep = 1, Value = data => 10 }; var outcome2 = new StepOutcome() { NextStep = 2, Value = data => 20 }; var step = A.Fake(); @@ -150,7 +150,7 @@ public void should_sleep_pointer() //arrange var persistenceData = new object(); var definition = new WorkflowDefinition(); - var pointer = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); @@ -171,8 +171,8 @@ public void should_branch_children() var branch = 10; var child = 2; var definition = new WorkflowDefinition(); - var pointer = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; - var childPointer = new ExecutionPointer(); + var pointer = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; + var childPointer = new ExecutionPointer() { Id = "2" }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); @@ -188,17 +188,16 @@ public void should_branch_children() A.CallTo(() => PointerFactory.BuildChildPointer(definition, pointer, child, branch)).MustHaveHappened(); instance.ExecutionPointers.Should().Contain(childPointer); } - private static WorkflowInstance GivenWorkflow(ExecutionPointer pointer) { return new WorkflowInstance { Status = WorkflowStatus.Runnable, - ExecutionPointers = new List() + ExecutionPointers = new ExecutionPointerCollection(new List() { pointer - } + }) }; } } diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 7f573be1d..27754465b 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -23,6 +23,7 @@ public class WorkflowExecutorFixture protected IWorkflowRegistry Registry; protected IExecutionResultProcessor ResultProcesser; protected ILifeCycleEventPublisher EventHub; + protected ICancellationProcessor CancellationProcessor; protected IServiceProvider ServiceProvider; protected IDateTimeProvider DateTimeProvider; protected WorkflowOptions Options; @@ -35,6 +36,7 @@ public WorkflowExecutorFixture() Registry = A.Fake(); ResultProcesser = A.Fake(); EventHub = A.Fake(); + CancellationProcessor = A.Fake(); DateTimeProvider = A.Fake(); Options = new WorkflowOptions(A.Fake()); @@ -45,7 +47,7 @@ public WorkflowExecutorFixture() var loggerFactory = new LoggerFactory(); loggerFactory.AddConsole(LogLevel.Debug); - Subject = new WorkflowExecutor(Registry, ServiceProvider, DateTimeProvider, ResultProcesser, EventHub, Options, loggerFactory); + Subject = new WorkflowExecutor(Registry, ServiceProvider, DateTimeProvider, ResultProcesser, EventHub, CancellationProcessor, Options, loggerFactory); } [Fact(DisplayName = "Should execute active step")] @@ -64,10 +66,10 @@ public void should_execute_active_step() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new List() + ExecutionPointers = new ExecutionPointerCollection(new List() { - new ExecutionPointer() { Active = true, StepId = 0 } - } + new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + }) }; //act @@ -94,10 +96,10 @@ public void should_trigger_step_hooks() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new List() + ExecutionPointers = new ExecutionPointerCollection(new List() { - new ExecutionPointer() { Active = true, StepId = 0 } - } + new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + }) }; //act @@ -125,10 +127,10 @@ public void should_not_execute_inactive_step() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new List() + ExecutionPointers = new ExecutionPointerCollection(new List() { - new ExecutionPointer() { Active = false, StepId = 0 } - } + new ExecutionPointer() { Id = "1", Active = false, StepId = 0 } + }) }; //act @@ -167,10 +169,10 @@ public void should_map_inputs() NextExecution = 0, Id = "001", Data = new DataClass() { Value1 = 5 }, - ExecutionPointers = new List() + ExecutionPointers = new ExecutionPointerCollection(new List() { - new ExecutionPointer() { Active = true, StepId = 0 } - } + new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + }) }; //act @@ -212,10 +214,10 @@ public void should_map_outputs() NextExecution = 0, Id = "001", Data = data, - ExecutionPointers = new List() + ExecutionPointers = new ExecutionPointerCollection(new List() { - new ExecutionPointer() { Active = true, StepId = 0 } - } + new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + }) }; //act @@ -260,10 +262,10 @@ public void should_map_outputs_dynamic() NextExecution = 0, Id = "001", Data = data, - ExecutionPointers = new List() + ExecutionPointers = new ExecutionPointerCollection(new List() { - new ExecutionPointer() { Active = true, StepId = 0 } - } + new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + }) }; //act @@ -312,10 +314,10 @@ public void should_map_outputs_object() NextExecution = 0, Id = "001", Data = data, - ExecutionPointers = new List() + ExecutionPointers = new ExecutionPointerCollection(new List() { - new ExecutionPointer() { Active = true, StepId = 0 } - } + new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + }) }; //act @@ -341,10 +343,10 @@ public void should_handle_step_exception() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new List() + ExecutionPointers = new ExecutionPointerCollection(new List() { - new ExecutionPointer() { Active = true, StepId = 0 } - } + new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + }) }; //act @@ -372,10 +374,10 @@ public void should_process_after_execution_iteration() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new List() + ExecutionPointers = new ExecutionPointerCollection(new List() { - new ExecutionPointer() { Active = true, StepId = 0 } - } + new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + }) }; //act @@ -385,6 +387,35 @@ public void should_process_after_execution_iteration() A.CallTo(() => step1.AfterWorkflowIteration(A.Ignored, A.Ignored, instance, A.Ignored)).MustHaveHappened(); } + [Fact(DisplayName = "Should process cancellations")] + public void should_process_cancellations() + { + //arrange + var step1Body = A.Fake(); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Persist(null)); + WorkflowStep step1 = BuildFakeStep(step1Body); + Given1StepWorkflow(step1, "Workflow", 1); + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + ExecutionPointers = new ExecutionPointerCollection(new List() + { + new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + }) + }; + + //act + Subject.Execute(instance); + + //assert + A.CallTo(() => CancellationProcessor.ProcessCancellations(instance, A.Ignored, A.Ignored)).MustHaveHappened(); + } + private void Given1StepWorkflow(WorkflowStep step1, string id, int version) { From bda699aa3a96c833200a4a899127c15a2fd804ca Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 14 Jan 2019 20:12:37 -0800 Subject: [PATCH 086/462] Update README.md --- src/providers/WorkflowCore.Providers.Redis/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.Redis/README.md b/src/providers/WorkflowCore.Providers.Redis/README.md index f25e50642..86545e714 100644 --- a/src/providers/WorkflowCore.Providers.Redis/README.md +++ b/src/providers/WorkflowCore.Providers.Redis/README.md @@ -11,13 +11,13 @@ This makes it possible to have a cluster of nodes processing your workflows. Install the NuGet package "WorkflowCore.Providers.Redis" Using Nuget package console - ``` - PM> Install-Package WorkflowCore.Providers.Redis - ``` +``` +PM> Install-Package WorkflowCore.Providers.Redis +``` Using .NET CLI - ``` - dotnet add package WorkflowCore.Providers.Redis - ``` +``` +dotnet add package WorkflowCore.Providers.Redis +``` ## Usage From 46ea0a87f48f92de8b5c0b5fa842acbd46b68a0e Mon Sep 17 00:00:00 2001 From: Erol KESKIN Date: Wed, 16 Jan 2019 16:35:59 +0300 Subject: [PATCH 087/462] Update README.md Syntax problems solved. Inserted MySQL Provider. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f78c34012..4feb8790a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ public class MyWorkflow : IWorkflow builder .StartWith() .Then() - .Then; + .Then(); } } ``` @@ -66,7 +66,7 @@ public class MyWorkflow : IWorkflow .StartWith() .Input(step => step.Email, data => data.Email) .Input(step => step.Password, data => data.Password) - .Output(data => data.UserId, step => step.UserId); + .Output(data => data.UserId, step => step.UserId) .Then() .WaitFor("confirmation", data => data.UserId) .Then() @@ -118,6 +118,7 @@ There are several persistence providers available as separate Nuget packages. * [SQL Server](src/providers/WorkflowCore.Persistence.SqlServer) * [PostgreSQL](src/providers/WorkflowCore.Persistence.PostgreSQL) * [Sqlite](src/providers/WorkflowCore.Persistence.Sqlite) +* [MySQL](src/providers/WorkflowCore.Persistence.MySQL) * Redis *(coming soon...)* ## Extensions From d46399a12c4ba5b18bd7bcbffdb6d287734e4f28 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 19 Jan 2019 07:55:18 -0800 Subject: [PATCH 088/462] Input / Output (#245) --- ReleaseNotes/next.md | 41 ++++++ WorkflowCore.sln | 3 +- src/WorkflowCore/Interface/IStepBuilder.cs | 16 ++- src/WorkflowCore/Interface/IStepParameter.cs | 11 ++ src/WorkflowCore/Models/ActionParameter.cs | 33 +++++ src/WorkflowCore/Models/DataMapping.cs | 11 -- src/WorkflowCore/Models/MemberMapParameter.cs | 61 ++++++++ src/WorkflowCore/Models/WorkflowStep.cs | 4 +- .../DefinitionStorage/DefinitionLoader.cs | 23 ++- .../FluentBuilders/ExpressionHelpers.cs | 55 ------- .../Services/FluentBuilders/StepBuilder.cs | 82 +++-------- src/WorkflowCore/Services/WorkflowExecutor.cs | 64 +-------- .../Services/UserTaskBuilder.cs | 6 +- .../WorkflowCore.Users.csproj | 6 +- .../PassingDataWorkflow2.cs | 2 +- src/samples/WorkflowCore.Sample03/Program.cs | 9 +- .../Scenarios/DynamicDataIOScenario.cs | 6 +- .../Scenarios/StoredScenario.cs | 31 +++- .../MemberMapParameterTests.cs | 105 ++++++++++++++ .../Services/WorkflowExecutorFixture.cs | 136 ++---------------- 20 files changed, 367 insertions(+), 338 deletions(-) create mode 100644 ReleaseNotes/next.md create mode 100644 src/WorkflowCore/Interface/IStepParameter.cs create mode 100644 src/WorkflowCore/Models/ActionParameter.cs delete mode 100644 src/WorkflowCore/Models/DataMapping.cs create mode 100644 src/WorkflowCore/Models/MemberMapParameter.cs delete mode 100644 src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs create mode 100644 test/WorkflowCore.UnitTests/MemberMapParameterTests.cs diff --git a/ReleaseNotes/next.md b/ReleaseNotes/next.md new file mode 100644 index 000000000..41fe49dae --- /dev/null +++ b/ReleaseNotes/next.md @@ -0,0 +1,41 @@ + +## Action Inputs / Outputs + +Added the action Input & Output overloads on the fluent step builder. + +```c# +Input(Action action); +``` + +This will allow one to manipulate properties on the step before it executes and properties on the data object after it executes, for example + +```c# +Input((step, data) => step.Value1 = data.Value1) +``` + +```c# +.Output((step, data) => data["Value3"] = step.Output) +``` + +```c# +.Output((step, data) => data.MyCollection.Add(step.Output)) +``` + +## Breaking changes + +The existing ability to assign values to entries in dictionaries or dynamic objects on `.Output` was problematic, +since it broke the ability to pass collections on the Output mappings. + + +```c# +.Output(data => data["Value3"], step => step.Output) +``` + +This feature has been removed, and it is advised to use the action Output API instead, for example + + +```c# +.Output((step, data) => data["Value3"] = step.Output) +``` + +This functionality remains intact for JSON defined workflows. \ No newline at end of file diff --git a/WorkflowCore.sln b/WorkflowCore.sln index a95be36f8..6ae8eb877 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -95,6 +95,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.6.8.md = ReleaseNotes\1.6.8.md ReleaseNotes\1.6.9.md = ReleaseNotes\1.6.9.md ReleaseNotes\1.7.0.md = ReleaseNotes\1.7.0.md + ReleaseNotes\next.md = ReleaseNotes\next.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" @@ -123,7 +124,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.MySQL", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.DynamoDB", "test\WorkflowCore.Tests.DynamoDB\WorkflowCore.Tests.DynamoDB.csproj", "{3ECEC028-7E2C-4983-B928-26C073B51BB7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Providers.Redis", "src\providers\WorkflowCore.Providers.Redis\WorkflowCore.Providers.Redis.csproj", "{435C6263-C6F8-4E93-B417-D861E9C22E18}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Providers.Redis", "src\providers\WorkflowCore.Providers.Redis\WorkflowCore.Providers.Redis.csproj", "{435C6263-C6F8-4E93-B417-D861E9C22E18}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/WorkflowCore/Interface/IStepBuilder.cs b/src/WorkflowCore/Interface/IStepBuilder.cs index aedd10b2e..df6f92404 100644 --- a/src/WorkflowCore/Interface/IStepBuilder.cs +++ b/src/WorkflowCore/Interface/IStepBuilder.cs @@ -77,6 +77,13 @@ public interface IStepBuilder /// IStepBuilder Input(Expression> stepProperty, Expression> value); + /// + /// Manipulate properties on the step before its executed. + /// + /// + /// + IStepBuilder Input(Action action); + /// /// Map properties on the workflow data object to properties on the step after the step executes /// @@ -86,6 +93,13 @@ public interface IStepBuilder /// IStepBuilder Output(Expression> dataProperty, Expression> value); + /// + /// Manipulate properties on the data object after the step executes + /// + /// + /// + IStepBuilder Output(Action action); + /// /// Wait here until to specified event is published /// @@ -222,4 +236,4 @@ public interface IStepBuilder IStepBuilder CancelCondition(Expression> cancelCondition, bool proceedAfterCancel = false); } -} \ No newline at end of file +} diff --git a/src/WorkflowCore/Interface/IStepParameter.cs b/src/WorkflowCore/Interface/IStepParameter.cs new file mode 100644 index 000000000..049ecdb25 --- /dev/null +++ b/src/WorkflowCore/Interface/IStepParameter.cs @@ -0,0 +1,11 @@ +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface IStepParameter + { + void AssignInput(object data, IStepBody body, IStepExecutionContext context); + void AssignOutput(object data, IStepBody body, IStepExecutionContext context); + } +} \ No newline at end of file diff --git a/src/WorkflowCore/Models/ActionParameter.cs b/src/WorkflowCore/Models/ActionParameter.cs new file mode 100644 index 000000000..e08edca13 --- /dev/null +++ b/src/WorkflowCore/Models/ActionParameter.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using WorkflowCore.Interface; + +namespace WorkflowCore.Models +{ + public class ActionParameter : IStepParameter + { + private readonly Action _action; + + public ActionParameter(Action action) + { + _action = action; + } + + private void Assign(object data, IStepBody step, IStepExecutionContext context) + { + _action.Invoke((TStepBody)step, (TData)data); + } + + public void AssignInput(object data, IStepBody body, IStepExecutionContext context) + { + Assign(data, body, context); + } + + public void AssignOutput(object data, IStepBody body, IStepExecutionContext context) + { + Assign(data, body, context); + } + } +} diff --git a/src/WorkflowCore/Models/DataMapping.cs b/src/WorkflowCore/Models/DataMapping.cs deleted file mode 100644 index b67daf23f..000000000 --- a/src/WorkflowCore/Models/DataMapping.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Linq.Expressions; - -namespace WorkflowCore.Models -{ - public class DataMapping - { - public LambdaExpression Source { get; set; } - - public LambdaExpression Target { get; set; } - } -} diff --git a/src/WorkflowCore/Models/MemberMapParameter.cs b/src/WorkflowCore/Models/MemberMapParameter.cs new file mode 100644 index 000000000..052f0bc1a --- /dev/null +++ b/src/WorkflowCore/Models/MemberMapParameter.cs @@ -0,0 +1,61 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using WorkflowCore.Interface; + +namespace WorkflowCore.Models +{ + public class MemberMapParameter : IStepParameter + { + private readonly LambdaExpression _source; + private readonly LambdaExpression _target; + + public MemberMapParameter(LambdaExpression source, LambdaExpression target) + { + if (target.Body.NodeType != ExpressionType.MemberAccess) + throw new NotSupportedException(); + + _source = source; + _target = target; + } + + private void Assign(object sourceObject, LambdaExpression sourceExpr, object targetObject, LambdaExpression targetExpr, IStepExecutionContext context) + { + object resolvedValue = null; + + switch (sourceExpr.Parameters.Count) + { + case 1: + resolvedValue = sourceExpr.Compile().DynamicInvoke(sourceObject); + break; + case 2: + resolvedValue = sourceExpr.Compile().DynamicInvoke(sourceObject, context); + break; + default: + throw new ArgumentException(); + } + + if (resolvedValue == null) + { + var defaultAssign = Expression.Lambda(Expression.Assign(targetExpr.Body, Expression.Default(targetExpr.ReturnType)), targetExpr.Parameters.Single()); + defaultAssign.Compile().DynamicInvoke(targetObject); + return; + } + + var valueExpr = Expression.Convert(Expression.Constant(resolvedValue), targetExpr.ReturnType); + var assign = Expression.Lambda(Expression.Assign(targetExpr.Body, valueExpr), targetExpr.Parameters.Single()); + assign.Compile().DynamicInvoke(targetObject); + } + + public void AssignInput(object data, IStepBody body, IStepExecutionContext context) + { + Assign(data, _source, body, _target, context); + } + + public void AssignOutput(object data, IStepBody body, IStepExecutionContext context) + { + Assign(body, _source, data, _target, context); + } + } +} diff --git a/src/WorkflowCore/Models/WorkflowStep.cs b/src/WorkflowCore/Models/WorkflowStep.cs index 1ef0db10c..00bcf26c4 100644 --- a/src/WorkflowCore/Models/WorkflowStep.cs +++ b/src/WorkflowCore/Models/WorkflowStep.cs @@ -20,9 +20,9 @@ public abstract class WorkflowStep public virtual List Outcomes { get; set; } = new List(); - public virtual List Inputs { get; set; } = new List(); + public virtual List Inputs { get; set; } = new List(); - public virtual List Outputs { get; set; } = new List(); + public virtual List Outputs { get; set; } = new List(); public virtual WorkflowErrorHandling? ErrorBehavior { get; set; } diff --git a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs index 8f4084ba3..6959990f6 100644 --- a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs +++ b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs @@ -177,11 +177,7 @@ private void AttachInputs(StepSourceV1 source, Type dataType, Type stepType, Wor var targetProperty = Expression.Property(stepParameter, input.Key); var targetExpr = Expression.Lambda(targetProperty, stepParameter); - step.Inputs.Add(new DataMapping - { - Source = sourceExpr, - Target = targetExpr - }); + step.Inputs.Add(new MemberMapParameter(sourceExpr, targetExpr)); } } @@ -200,20 +196,23 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo if (propertyInfo != null) { targetProperty = Expression.Property(dataParameter, propertyInfo); + var targetExpr = Expression.Lambda(targetProperty, dataParameter); + step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr)); } else { // If we did not find a matching property try to find a Indexer with string parameter propertyInfo = dataType.GetProperty("Item"); targetProperty = Expression.Property(dataParameter, propertyInfo, Expression.Constant(output.Key)); - } - var targetExpr = Expression.Lambda(targetProperty, dataParameter); + + Action acn = (pStep, pData) => + { + object resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); ; + propertyInfo.SetValue(pData, resolvedValue, new object[] { output.Key }); + }; - step.Outputs.Add(new DataMapping - { - Source = sourceExpr, - Target = targetExpr - }); + step.Outputs.Add(new ActionParameter(acn)); + } } } diff --git a/src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs b/src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs deleted file mode 100644 index ca1ba4f9a..000000000 --- a/src/WorkflowCore/Services/FluentBuilders/ExpressionHelpers.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; - -namespace WorkflowCore.Services.FluentBuilders -{ - internal static class ExpressionHelpers - { - public static Expression> CreateSetter( - Expression> getterExpression) - { - var valueParameter = Expression.Parameter(typeof(TParam), "value"); - Expression assignment; - if (getterExpression.Body is MethodCallExpression callExpression && callExpression.Method.Name == "get_Item") - { - //Get Matching setter method for the indexer - var parameterTypes = callExpression.Method.GetParameters() - .Select(p => p.ParameterType) - .ToArray(); - var itemProperty = callExpression.Method.DeclaringType.GetProperty("Item", typeof(TParam), parameterTypes); - - assignment = Expression.Call(callExpression.Object, itemProperty.SetMethod, callExpression.Arguments.Concat(new[] { valueParameter })); - } - else - { - assignment = Expression.Assign(getterExpression.Body, valueParameter); - } - return Expression.Lambda>(assignment, valueParameter); - } - - public static LambdaExpression CreateSetter(LambdaExpression getterExpression) - { - var valueParameter = Expression.Parameter(getterExpression.ReturnType, "value"); - Expression assignment; - if (getterExpression.Body is MethodCallExpression callExpression && callExpression.Method.Name == "get_Item") - { - //Get Matching setter method for the indexer - var parameterTypes = callExpression.Method.GetParameters() - .Select(p => p.ParameterType) - .ToArray(); - var itemProperty = callExpression.Method.DeclaringType.GetProperty("Item", valueParameter.Type, parameterTypes); - - assignment = Expression.Call(callExpression.Object, itemProperty.SetMethod, callExpression.Arguments.Concat(new[] { valueParameter })); - } - else - { - assignment = Expression.Assign(getterExpression.Body, valueParameter); - } - - var parameters = getterExpression.Parameters.Concat(new[] {valueParameter}).ToArray(); - return Expression.Lambda(assignment, parameters); - } - } -} diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index f648a418f..37ca0cd29 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -84,28 +84,31 @@ public IStepOutcomeBuilder When(object outcomeValue, string label = null) public IStepBuilder Input(Expression> stepProperty, Expression> value) { - var mapping = new DataMapping(); - mapping.Source = value; - mapping.Target = stepProperty; - Step.Inputs.Add(mapping); + Step.Inputs.Add(new MemberMapParameter(value, stepProperty)); return this; } public IStepBuilder Input(Expression> stepProperty, Expression> value) { - var mapping = new DataMapping(); - mapping.Source = value; - mapping.Target = stepProperty; - Step.Inputs.Add(mapping); + Step.Inputs.Add(new MemberMapParameter(value, stepProperty)); + return this; + } + + public IStepBuilder Input(Action action) + { + Step.Inputs.Add(new ActionParameter(action)); return this; } public IStepBuilder Output(Expression> dataProperty, Expression> value) { - var mapping = new DataMapping(); - mapping.Source = value; - mapping.Target = dataProperty; - Step.Outputs.Add(mapping); + Step.Outputs.Add(new MemberMapParameter(value, dataProperty)); + return this; + } + + public IStepBuilder Output(Action action) + { + Step.Outputs.Add(new ActionParameter(action)); return this; } @@ -208,14 +211,7 @@ public IStepBuilder Delay(Expression> period var newStep = new WorkflowStep(); Expression> inputExpr = (x => x.Period); - - var mapping = new DataMapping() - { - Source = period, - Target = inputExpr - }; - - newStep.Inputs.Add(mapping); + newStep.Inputs.Add(new MemberMapParameter(period, inputExpr)); WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); @@ -229,13 +225,7 @@ public IContainerStepBuilder ForEach(Expression(); Expression> inputExpr = (x => x.Collection); - - var mapping = new DataMapping() - { - Source = collection, - Target = inputExpr - }; - newStep.Inputs.Add(mapping); + newStep.Inputs.Add(new MemberMapParameter(collection, inputExpr)); WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); @@ -250,13 +240,7 @@ public IContainerStepBuilder While(Expression(); Expression> inputExpr = (x => x.Condition); - - var mapping = new DataMapping() - { - Source = condition, - Target = inputExpr - }; - newStep.Inputs.Add(mapping); + newStep.Inputs.Add(new MemberMapParameter(condition, inputExpr)); WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); @@ -271,14 +255,7 @@ public IContainerStepBuilder If(Expression> con var newStep = new WorkflowStep(); Expression> inputExpr = (x => x.Condition); - - var mapping = new DataMapping() - { - Source = condition, - Target = inputExpr - }; - - newStep.Inputs.Add(mapping); + newStep.Inputs.Add(new MemberMapParameter(condition, inputExpr)); WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); @@ -292,13 +269,7 @@ public IContainerStepBuilder When(Expression(); Expression> inputExpr = (x => x.ExpectedOutcome); - var mapping = new DataMapping() - { - Source = outcomeValue, - Target = inputExpr - }; - - newStep.Inputs.Add(mapping); + newStep.Inputs.Add(new MemberMapParameter(outcomeValue, inputExpr)); IStepBuilder switchBuilder; @@ -353,14 +324,7 @@ public IContainerStepBuilder Schedule(Expression(); Expression> inputExpr = (x => x.Interval); - - var mapping = new DataMapping() - { - Source = time, - Target = inputExpr - }; - - newStep.Inputs.Add(mapping); + newStep.Inputs.Add(new MemberMapParameter(time, inputExpr)); WorkflowBuilder.AddStep(newStep); var stepBuilder = new ReturnStepBuilder(WorkflowBuilder, newStep, this); @@ -376,8 +340,8 @@ public IContainerStepBuilder Recur(Expression> intervalExpr = (x => x.Interval); Expression> untilExpr = (x => x.StopCondition); - newStep.Inputs.Add(new DataMapping() { Source = interval, Target = intervalExpr }); - newStep.Inputs.Add(new DataMapping() { Source = until, Target = untilExpr }); + newStep.Inputs.Add(new MemberMapParameter(interval, intervalExpr)); + newStep.Inputs.Add(new MemberMapParameter(until, untilExpr)); WorkflowBuilder.AddStep(newStep); var stepBuilder = new ReturnStepBuilder(WorkflowBuilder, newStep, this); diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index c2b643aee..a3e5c0c46 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -2,14 +2,11 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; -using System.Reflection; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Models.LifeCycleEvents; -using WorkflowCore.Services.FluentBuilders; namespace WorkflowCore.Services { @@ -117,7 +114,9 @@ public async Task Execute(WorkflowInstance workflow) Item = pointer.ContextItem }; - ProcessInputs(workflow, step, body, context); + foreach (var input in step.Inputs) + input.AssignInput(workflow.Data, body, context); + switch (step.BeforeExecute(wfResult, context, pointer, body)) { @@ -133,7 +132,8 @@ public async Task Execute(WorkflowInstance workflow) if (result.Proceed) { - ProcessOutputs(workflow, step, body, context); + foreach (var output in step.Outputs) + output.AssignOutput(workflow.Data, body, context); } _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); @@ -174,59 +174,7 @@ public async Task Execute(WorkflowInstance workflow) return wfResult; } - - private void ProcessInputs(WorkflowInstance workflow, WorkflowStep step, IStepBody body, IStepExecutionContext context) - { - //TODO: Move to own class - foreach (var input in step.Inputs) - { - var member = (input.Target.Body as MemberExpression); - object resolvedValue = null; - - switch (input.Source.Parameters.Count) - { - case 1: - resolvedValue = input.Source.Compile().DynamicInvoke(workflow.Data); - break; - case 2: - resolvedValue = input.Source.Compile().DynamicInvoke(workflow.Data, context); - break; - default: - throw new ArgumentException(); - } - - step.BodyType.GetProperty(member.Member.Name).SetValue(body, resolvedValue); - } - } - - private void ProcessOutputs(WorkflowInstance workflow, WorkflowStep step, IStepBody body, IStepExecutionContext context) - { - foreach (var output in step.Outputs) - { - var resolvedValue = output.Source.Compile().DynamicInvoke(body); - var data = workflow.Data; - var setter = ExpressionHelpers.CreateSetter(output.Target); - var targetType = setter.Parameters.Last().Type; - - var convertedValue = resolvedValue; - // We need to make sure the resolvedValue is of the correct type. - // However if the targetType is object we don't need to do anything and in some cases Convert.ChangeType will throw. - if (targetType != typeof(object)) - { - convertedValue = Convert.ChangeType(resolvedValue, targetType); - } - - if (setter.Parameters.Count == 2) - { - setter.Compile().DynamicInvoke(data, convertedValue); - } - else - { - setter.Compile().DynamicInvoke(data, context, convertedValue); - } - } - } - + private void ProcessAfterExecutionIteration(WorkflowInstance workflow, WorkflowDefinition workflowDef, WorkflowExecutorResult workflowResult) { var pointers = workflow.ExecutionPointers.Where(x => x.EndTime == null); diff --git a/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs b/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs index 2bb232470..175182bf7 100644 --- a/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs +++ b/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs @@ -26,11 +26,7 @@ public IUserTaskReturnBuilder WithOption(string value, string label) var newStep = new WorkflowStep(); Expression> inputExpr = (x => x.ExpectedOutcome); Expression> valueExpr = (x => value); - var mapping = new DataMapping() - { - Source = valueExpr, - Target = inputExpr - }; + var mapping = new MemberMapParameter(valueExpr, inputExpr); newStep.Inputs.Add(mapping); WorkflowBuilder.AddStep(newStep); diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 15f1cb68d..8c7bd192d 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -18,9 +18,9 @@ false false Provides extensions for Workflow Core to enable human workflows. - 1.7.0 - 1.7.0.0 - 1.7.0.0 + 1.8.0 + 1.8.0.0 + 1.8.0.0 diff --git a/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs b/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs index b731d53e2..500c8ad74 100644 --- a/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs +++ b/src/samples/WorkflowCore.Sample03/PassingDataWorkflow2.cs @@ -19,7 +19,7 @@ public void Build(IWorkflowBuilder> builder) .Then() .Input(step => step.Input1, data => data["Value1"]) .Input(step => step.Input2, data => data["Value2"]) - .Output(data => data["Value3"], step => step.Output) + .Output((step, data) => data["Value3"] = step.Output) .Then() .Name("Print custom message") .Input(step => step.Message, data => "The answer is " + data["Value3"].ToString()) diff --git a/src/samples/WorkflowCore.Sample03/Program.cs b/src/samples/WorkflowCore.Sample03/Program.cs index 4b65d75a2..1f17ae507 100644 --- a/src/samples/WorkflowCore.Sample03/Program.cs +++ b/src/samples/WorkflowCore.Sample03/Program.cs @@ -19,6 +19,7 @@ public static void Main(string[] args) //start the workflow host var host = serviceProvider.GetService(); host.RegisterWorkflow(); + host.RegisterWorkflow>(); host.Start(); var initialData = new MyDataClass @@ -27,15 +28,13 @@ public static void Main(string[] args) Value2 = 3 }; - host.StartWorkflow("PassingDataWorkflow", 1, initialData); + //host.StartWorkflow("PassingDataWorkflow", 1, initialData); - host.RegisterWorkflow>(); - //host.Start(); var initialData2 = new Dictionary { - ["Value1"] = 2, - ["Value2"] = 3 + ["Value1"] = 7, + ["Value2"] = 2 }; host.StartWorkflow("PassingDataWorkflow2", 1, initialData2); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs index 779e3f1dd..4c1c013ba 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs @@ -44,9 +44,9 @@ public void Build(IWorkflowBuilder builder) { builder .StartWith() - .Input(step => step.Input1, data => data.Value1) - .Input(step => step.Input2, data => data.Value2) - .Output(data => data["Value3"], step => step.Output); + .Input(step => step.Input1, data => data.Value1) + .Input(step => step.Input2, data => data.Value2) + .Output((step, data) => data["Value3"] = step.Output); } } diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredScenario.cs index f5636751a..e854c45cb 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredScenario.cs @@ -18,7 +18,7 @@ public StoredScenario() } [Fact(DisplayName = "Execute workflow from stored JSON definition")] - public void Scenario() + public void should_execute_json_workflow() { var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionJson(), new CounterBoard() { Flag1 = true, Flag2 = true }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); @@ -33,5 +33,34 @@ public void Scenario() data.Counter5.Should().Be(0); data.Counter6.Should().Be(1); } + + [Fact] + public void should_execute_json_workflow_with_dynamic_data() + { + var initialData = new DynamicData + { + ["Flag1"] = true, + ["Flag2"] = true, + ["Counter1"] = 0, + ["Counter2"] = 0, + ["Counter3"] = 0, + ["Counter4"] = 0, + ["Counter5"] = 0, + ["Counter6"] = 0 + }; + + var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionDynamicJson(), initialData); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + var data = GetData(workflowId); + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + data["Counter1"].Should().Be(1); + data["Counter2"].Should().Be(1); + data["Counter3"].Should().Be(1); + data["Counter4"].Should().Be(1); + data["Counter5"].Should().Be(0); + data["Counter6"].Should().Be(1); + } } } diff --git a/test/WorkflowCore.UnitTests/MemberMapParameterTests.cs b/test/WorkflowCore.UnitTests/MemberMapParameterTests.cs new file mode 100644 index 000000000..c25b57857 --- /dev/null +++ b/test/WorkflowCore.UnitTests/MemberMapParameterTests.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; + +namespace WorkflowCore.UnitTests +{ + public class MemberMapParameterTests + { + + [Fact] + public void should_assign_input() + { + Expression> memberExpr = (x => x.Value1); + Expression> valueExpr = (x => x.Value1); + var subject = new MemberMapParameter(valueExpr, memberExpr); + var data = new MyData() + { + Value1 = 5 + }; + var step = new MyStep(); + + subject.AssignInput(data, step, new StepExecutionContext()); + + step.Value1.Should().Be(data.Value1); + } + + [Fact] + public void should_assign_output() + { + Expression> memberExpr = (x => x.Value1); + Expression> valueExpr = (x => x.Value1); + var subject = new MemberMapParameter(valueExpr, memberExpr); + var data = new MyData(); + var step = new MyStep() + { + Value1 = 5 + }; + + subject.AssignOutput(data, step, new StepExecutionContext()); + + data.Value1.Should().Be(step.Value1); + } + + [Fact] + public void should_convert_input() + { + Expression> memberExpr = (x => x.Value2); + Expression> valueExpr = (x => x.Value1); + var subject = new MemberMapParameter(valueExpr, memberExpr); + + var data = new MyData() + { + Value1 = 5 + }; + + var step = new MyStep(); + + subject.AssignInput(data, step, new StepExecutionContext()); + + step.Value2.Should().Be(data.Value1); + } + + [Fact] + public void should_convert_output() + { + Expression> memberExpr = (x => x.Value2); + Expression> valueExpr = (x => x.Value1); + var subject = new MemberMapParameter(valueExpr, memberExpr); + + var data = new MyData() + { + Value1 = 5 + }; + + var step = new MyStep(); + + subject.AssignOutput(data, step, new StepExecutionContext()); + + data.Value2.Should().Be(step.Value1); + } + + + class MyData + { + public int Value1 { get; set; } + public object Value2 { get; set; } + } + + class MyStep : IStepBody + { + public int Value1 { get; set; } + public object Value2 { get; set; } + + public Task RunAsync(IStepExecutionContext context) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 27754465b..d94ed3416 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -144,20 +144,15 @@ public void should_not_execute_inactive_step() public void should_map_inputs() { //arrange - Expression> p1 = x => x.Property1; - Expression> v1 = (x, context) => x.Value1; + var param = A.Fake(); var step1Body = A.Fake(); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); - WorkflowStep step1 = BuildFakeStep(step1Body, new List() + WorkflowStep step1 = BuildFakeStep(step1Body, new List() { - new DataMapping() - { - Source = v1, - Target = p1 - } + param } - , new List()); + , new List()); Given1StepWorkflow(step1, "Workflow", 1); @@ -179,26 +174,22 @@ public void should_map_inputs() Subject.Execute(instance); //assert - step1Body.Property1.Should().Be(5); + A.CallTo(() => param.AssignInput(A.Ignored, step1Body, A.Ignored)) + .MustHaveHappened(); } [Fact(DisplayName = "Should map outputs")] public void should_map_outputs() { - //arrange - Expression> p1 = x => x.Property1; - Expression> v1 = (x, context) => x.Value1; + //arrange + var param = A.Fake(); var step1Body = A.Fake(); A.CallTo(() => step1Body.Property1).Returns(7); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); - WorkflowStep step1 = BuildFakeStep(step1Body, new List(), new List() + WorkflowStep step1 = BuildFakeStep(step1Body, new List(), new List() { - new DataMapping() - { - Source = p1, - Target = v1 - } + param } ); @@ -224,108 +215,11 @@ public void should_map_outputs() Subject.Execute(instance); //assert - data.Value1.Should().Be(7); - } - - [Fact(DisplayName = "Should map dynamic outputs")] - public void should_map_outputs_dynamic() - { - //arrange - Expression> p1 = x => x.Property1; - Expression> v1 = (x, context) => x["Value1"]; - - var step1Body = A.Fake(); - A.CallTo(() => step1Body.Property1).Returns(7); - A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); - WorkflowStep step1 = BuildFakeStep(step1Body, new List(), new List() - { - new DataMapping() - { - Source = p1, - Target = v1 - } - } - ); - - Given1StepWorkflow(step1, "Workflow", 1); - - var data = new DynamicDataClass() - { - ["Value1"] = 5 - }; - - var instance = new WorkflowInstance - { - WorkflowDefinitionId = "Workflow", - Version = 1, - Status = WorkflowStatus.Runnable, - NextExecution = 0, - Id = "001", - Data = data, - ExecutionPointers = new ExecutionPointerCollection(new List() - { - new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } - }) - }; - - //act - Subject.Execute(instance); - - //assert - data["Value1"].Should().Be(7); + A.CallTo(() => param.AssignOutput(data, step1Body, A.Ignored)) + .MustHaveHappened(); } - /// - /// This test verifies that storing an object that does not implement IConvertable, in a step variable of type object works. - /// The problem is that calling for example Convert.ChangeType(new DataClass(), typeof(object)) throws, even though the convertion should be trivial. - /// - [Fact(DisplayName = "Should map object outputs, without calling Convert.ChangeType")] - public void should_map_outputs_object() - { - //arrange - Expression> p1 = x => x.Property4; - Expression> v1 = (x, context) => x.Value4; - - var step1Body = A.Fake(); - A.CallTo(() => step1Body.Property4).Returns(new DataClass()); - A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); - WorkflowStep step1 = BuildFakeStep(step1Body, new List(), new List() - { - new DataMapping() - { - Source = p1, - Target = v1 - } - } - ); - - Given1StepWorkflow(step1, "Workflow", 1); - - var data = new DataClass() - { - Value4 = 4 - }; - - var instance = new WorkflowInstance - { - WorkflowDefinitionId = "Workflow", - Version = 1, - Status = WorkflowStatus.Runnable, - NextExecution = 0, - Id = "001", - Data = data, - ExecutionPointers = new ExecutionPointerCollection(new List() - { - new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } - }) - }; - - //act - Subject.Execute(instance); - - //assert - data.Value4.Should().BeOfType(); - } + [Fact(DisplayName = "Should handle step exception")] public void should_handle_step_exception() @@ -434,10 +328,10 @@ private void Given1StepWorkflow(WorkflowStep step1, string id, int version) private WorkflowStep BuildFakeStep(IStepBody stepBody) { - return BuildFakeStep(stepBody, new List(), new List()); + return BuildFakeStep(stepBody, new List(), new List()); } - private WorkflowStep BuildFakeStep(IStepBody stepBody, List inputs, List outputs) + private WorkflowStep BuildFakeStep(IStepBody stepBody, List inputs, List outputs) { var result = A.Fake(); A.CallTo(() => result.Id).Returns(0); From 8a6d1ff596a1ecacf71d80ed38481ec0a557ef38 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 19 Jan 2019 08:18:58 -0800 Subject: [PATCH 089/462] Search Index API & Elasticsearch support (#243) --- README.md | 6 + WorkflowCore.sln | 14 ++ .../Interface/IPersistenceProvider.cs | 1 + src/WorkflowCore/Interface/ISearchIndex.cs | 20 ++ src/WorkflowCore/Interface/ISearchable.cs | 11 ++ src/WorkflowCore/Models/Search/Page.cs | 12 ++ .../Models/Search/SearchFilter.cs | 153 +++++++++++++++ src/WorkflowCore/Models/Search/StepInfo.cs | 13 ++ .../Models/Search/WorkflowSearchResult.cs | 44 +++++ src/WorkflowCore/Models/WorkflowOptions.cs | 7 + .../ServiceCollectionExtensions.cs | 4 +- .../BackgroundTasks/WorkflowConsumer.cs | 5 +- .../DefaultProviders/NullSearchIndex.cs | 33 ++++ .../Services/WorkflowController.cs | 8 +- src/WorkflowCore/Services/WorkflowHost.cs | 6 +- src/WorkflowCore/WorkflowCore.csproj | 6 +- .../Models/WorkflowSearchModel.cs | 126 ++++++++++++ .../README.md | 132 +++++++++++++ .../ServiceCollectionExtensions.cs | 17 ++ .../Services/ElasticsearchIndexer.cs | 141 ++++++++++++++ ...orkflowCore.Providers.Elasticsearch.csproj | 24 +++ .../WorkflowCore.Sample03/MyDataClass.cs | 3 + .../EventSampleWorkflow.cs | 4 +- .../WorkflowCore.Sample04/MyDataClass.cs | 2 +- src/samples/WorkflowCore.Sample07/web.config | 10 +- test/ScratchPad/Program.cs | 107 +++-------- test/ScratchPad/ScratchPad.csproj | 5 + .../SearchIndexTests.cs | 181 ++++++++++++++++++ .../ElasticsearchDockerSetup.cs | 44 +++++ .../ElasticsearchIndexerTests.cs | 27 +++ .../WorkflowCore.Tests.Elasticsearch.csproj | 22 +++ 31 files changed, 1092 insertions(+), 96 deletions(-) create mode 100644 src/WorkflowCore/Interface/ISearchIndex.cs create mode 100644 src/WorkflowCore/Interface/ISearchable.cs create mode 100644 src/WorkflowCore/Models/Search/Page.cs create mode 100644 src/WorkflowCore/Models/Search/SearchFilter.cs create mode 100644 src/WorkflowCore/Models/Search/StepInfo.cs create mode 100644 src/WorkflowCore/Models/Search/WorkflowSearchResult.cs create mode 100644 src/WorkflowCore/Services/DefaultProviders/NullSearchIndex.cs create mode 100644 src/providers/WorkflowCore.Providers.Elasticsearch/Models/WorkflowSearchModel.cs create mode 100644 src/providers/WorkflowCore.Providers.Elasticsearch/README.md create mode 100644 src/providers/WorkflowCore.Providers.Elasticsearch/ServiceCollectionExtensions.cs create mode 100644 src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs create mode 100644 src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj create mode 100644 test/WorkflowCore.IntegrationTests/SearchIndexTests.cs create mode 100644 test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs create mode 100644 test/WorkflowCore.Tests.Elasticsearch/ElasticsearchIndexerTests.cs create mode 100644 test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj diff --git a/README.md b/README.md index f78c34012..c182dda27 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,12 @@ There are several persistence providers available as separate Nuget packages. * [Sqlite](src/providers/WorkflowCore.Persistence.Sqlite) * Redis *(coming soon...)* +## Search + +A search index provider can be plugged in to Workflow Core, enabling you to index your workflows and search against the data and state of them. +These are also available as separate Nuget packages. +* [Elasticsearch](src/providers/WorkflowCore.Providers.Elasticsearch) + ## Extensions * [User (human) workflows](src/extensions/WorkflowCore.Users) diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 6ae8eb877..053f46bcf 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -126,6 +126,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.DynamoDB EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Providers.Redis", "src\providers\WorkflowCore.Providers.Redis\WorkflowCore.Providers.Redis.csproj", "{435C6263-C6F8-4E93-B417-D861E9C22E18}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Providers.Elasticsearch", "src\providers\WorkflowCore.Providers.Elasticsearch\WorkflowCore.Providers.Elasticsearch.csproj", "{F6348170-B695-4D97-BAE6-4F0F643F3BEF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.Elasticsearch", "test\WorkflowCore.Tests.Elasticsearch\WorkflowCore.Tests.Elasticsearch.csproj", "{44644716-0CE8-4837-B189-AB65AE2106AA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -316,6 +320,14 @@ Global {435C6263-C6F8-4E93-B417-D861E9C22E18}.Debug|Any CPU.Build.0 = Debug|Any CPU {435C6263-C6F8-4E93-B417-D861E9C22E18}.Release|Any CPU.ActiveCfg = Release|Any CPU {435C6263-C6F8-4E93-B417-D861E9C22E18}.Release|Any CPU.Build.0 = Release|Any CPU + {F6348170-B695-4D97-BAE6-4F0F643F3BEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6348170-B695-4D97-BAE6-4F0F643F3BEF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6348170-B695-4D97-BAE6-4F0F643F3BEF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6348170-B695-4D97-BAE6-4F0F643F3BEF}.Release|Any CPU.Build.0 = Release|Any CPU + {44644716-0CE8-4837-B189-AB65AE2106AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44644716-0CE8-4837-B189-AB65AE2106AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44644716-0CE8-4837-B189-AB65AE2106AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44644716-0CE8-4837-B189-AB65AE2106AA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -370,6 +382,8 @@ Global {DF7F7ECA-1771-40C9-9CD0-AFEFC44E60DE} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {3ECEC028-7E2C-4983-B928-26C073B51BB7} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {435C6263-C6F8-4E93-B417-D861E9C22E18} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} + {F6348170-B695-4D97-BAE6-4F0F643F3BEF} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} + {44644716-0CE8-4837-B189-AB65AE2106AA} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/WorkflowCore/Interface/IPersistenceProvider.cs b/src/WorkflowCore/Interface/IPersistenceProvider.cs index 709d20c3b..a71128f49 100644 --- a/src/WorkflowCore/Interface/IPersistenceProvider.cs +++ b/src/WorkflowCore/Interface/IPersistenceProvider.cs @@ -17,6 +17,7 @@ public interface IPersistenceProvider Task> GetRunnableInstances(DateTime asAt); + [Obsolete] Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take); Task GetWorkflowInstance(string Id); diff --git a/src/WorkflowCore/Interface/ISearchIndex.cs b/src/WorkflowCore/Interface/ISearchIndex.cs new file mode 100644 index 000000000..64bd075f6 --- /dev/null +++ b/src/WorkflowCore/Interface/ISearchIndex.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using WorkflowCore.Models; +using WorkflowCore.Models.Search; + +namespace WorkflowCore.Interface +{ + public interface ISearchIndex + { + Task IndexWorkflow(WorkflowInstance workflow); + + Task> Search(string terms, int skip, int take, params SearchFilter[] filters); + + Task Start(); + + Task Stop(); + } +} diff --git a/src/WorkflowCore/Interface/ISearchable.cs b/src/WorkflowCore/Interface/ISearchable.cs new file mode 100644 index 000000000..9ceec14e2 --- /dev/null +++ b/src/WorkflowCore/Interface/ISearchable.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Interface +{ + public interface ISearchable + { + IEnumerable GetSearchTokens(); + } +} diff --git a/src/WorkflowCore/Models/Search/Page.cs b/src/WorkflowCore/Models/Search/Page.cs new file mode 100644 index 000000000..63658fd37 --- /dev/null +++ b/src/WorkflowCore/Models/Search/Page.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.Search +{ + public class Page + { + public ICollection Data { get; set; } + public long Total { get; set; } + } +} diff --git a/src/WorkflowCore/Models/Search/SearchFilter.cs b/src/WorkflowCore/Models/Search/SearchFilter.cs new file mode 100644 index 000000000..fd7d8c7a4 --- /dev/null +++ b/src/WorkflowCore/Models/Search/SearchFilter.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; + +namespace WorkflowCore.Models.Search +{ + public abstract class SearchFilter + { + public bool IsData { get; set; } + public Type DataType { get; set; } + public Expression Property { get; set; } + + static Func Lambda(Func del) + { + return del; + } + } + + public class ScalarFilter : SearchFilter + { + public object Value { get; set; } + + public static SearchFilter Equals(Expression> property, object value) => new ScalarFilter() + { + Property = property, + Value = value + }; + + public static SearchFilter Equals(Expression> property, object value) => new ScalarFilter() + { + IsData = true, + DataType = typeof(T), + Property = property, + Value = value + }; + } + + public class DateRangeFilter : SearchFilter + { + public DateTime? BeforeValue { get; set; } + public DateTime? AfterValue { get; set; } + + public static DateRangeFilter Before(Expression> property, DateTime value) => new DateRangeFilter() + { + Property = property, + BeforeValue = value + }; + + public static DateRangeFilter After(Expression> property, DateTime value) => new DateRangeFilter() + { + Property = property, + AfterValue = value + }; + + public static DateRangeFilter Between(Expression> property, DateTime start, DateTime end) => new DateRangeFilter() + { + Property = property, + BeforeValue = end, + AfterValue = start + }; + + public static DateRangeFilter Before(Expression> property, DateTime value) => new DateRangeFilter() + { + IsData = true, + DataType = typeof(T), + Property = property, + BeforeValue = value + }; + + public static DateRangeFilter After(Expression> property, DateTime value) => new DateRangeFilter() + { + IsData = true, + DataType = typeof(T), + Property = property, + AfterValue = value + }; + + public static DateRangeFilter Between(Expression> property, DateTime start, DateTime end) => new DateRangeFilter() + { + IsData = true, + DataType = typeof(T), + Property = property, + BeforeValue = end, + AfterValue = start + }; + } + + public class NumericRangeFilter : SearchFilter + { + public double? LessValue { get; set; } + public double? GreaterValue { get; set; } + + public static NumericRangeFilter LessThan(Expression> property, double value) => new NumericRangeFilter() + { + Property = property, + LessValue = value + }; + + public static NumericRangeFilter GreaterThan(Expression> property, double value) => new NumericRangeFilter() + { + Property = property, + GreaterValue = value + }; + + public static NumericRangeFilter Between(Expression> property, double start, double end) => new NumericRangeFilter() + { + Property = property, + LessValue = end, + GreaterValue = start + }; + + public static NumericRangeFilter LessThan(Expression> property, double value) => new NumericRangeFilter() + { + IsData = true, + DataType = typeof(T), + Property = property, + LessValue = value + }; + + public static NumericRangeFilter GreaterThan(Expression> property, double value) => new NumericRangeFilter() + { + IsData = true, + DataType = typeof(T), + Property = property, + GreaterValue = value + }; + + public static NumericRangeFilter Between(Expression> property, double start, double end) => new NumericRangeFilter() + { + IsData = true, + DataType = typeof(T), + Property = property, + LessValue = end, + GreaterValue = start + }; + } + + public class StatusFilter : ScalarFilter + { + protected StatusFilter() + { + Expression> lambda = (WorkflowSearchResult x) => x.Status; + Property = lambda; + } + + public static StatusFilter Equals(WorkflowStatus value) => new StatusFilter() + { + Value = value.ToString() + }; + } + +} diff --git a/src/WorkflowCore/Models/Search/StepInfo.cs b/src/WorkflowCore/Models/Search/StepInfo.cs new file mode 100644 index 000000000..b17ccedd0 --- /dev/null +++ b/src/WorkflowCore/Models/Search/StepInfo.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models.Search +{ + public class StepInfo + { + public int StepId { get; set; } + + public string Name { get; set; } + } +} diff --git a/src/WorkflowCore/Models/Search/WorkflowSearchResult.cs b/src/WorkflowCore/Models/Search/WorkflowSearchResult.cs new file mode 100644 index 000000000..6870d36ab --- /dev/null +++ b/src/WorkflowCore/Models/Search/WorkflowSearchResult.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using WorkflowCore.Interface; + +namespace WorkflowCore.Models.Search +{ + public class WorkflowSearchResult + { + public string Id { get; set; } + + public string WorkflowDefinitionId { get; set; } + + public int Version { get; set; } + + public string Description { get; set; } + + public string Reference { get; set; } + + public DateTime? NextExecutionUtc { get; set; } + + public WorkflowStatus Status { get; set; } + + public object Data { get; set; } + + public DateTime CreateTime { get; set; } + + public DateTime? CompleteTime { get; set; } + + public ICollection WaitingSteps { get; set; } = new HashSet(); + + public ICollection SleepingSteps { get; set; } = new HashSet(); + + public ICollection FailedSteps { get; set; } = new HashSet(); + + + } + + public class WorkflowSearchResult : WorkflowSearchResult + { + public new TData Data { get; set; } + } + +} diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index f5e511dd3..16d524b99 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -12,6 +12,7 @@ public class WorkflowOptions internal Func QueueFactory; internal Func LockFactory; internal Func EventHubFactory; + internal Func SearchIndexFactory; internal TimeSpan PollInterval; internal TimeSpan IdleTime; internal TimeSpan ErrorRetryInterval; @@ -28,6 +29,7 @@ public WorkflowOptions(IServiceCollection services) QueueFactory = new Func(sp => new SingleNodeQueueProvider()); LockFactory = new Func(sp => new SingleNodeLockProvider()); PersistanceFactory = new Func(sp => new MemoryPersistenceProvider()); + SearchIndexFactory = new Func(sp => new NullSearchIndex()); EventHubFactory = new Func(sp => new SingleNodeEventHub(sp.GetService())); } @@ -51,6 +53,11 @@ public void UseEventHub(Func factory) EventHubFactory = factory; } + public void UseSearchIndex(Func factory) + { + SearchIndexFactory = factory; + } + public void UsePollInterval(TimeSpan interval) { PollInterval = interval; diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 8b3a77058..aebae27a1 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -28,9 +28,11 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(options.QueueFactory); services.AddSingleton(options.LockFactory); services.AddSingleton(options.EventHubFactory); + services.AddSingleton(options.SearchIndexFactory); + services.AddSingleton(); services.AddSingleton(options); - services.AddSingleton(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 296f8deb3..dcad34e18 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -12,17 +12,19 @@ internal class WorkflowConsumer : QueueConsumer, IBackgroundTask { private readonly IDistributedLockProvider _lockProvider; private readonly IDateTimeProvider _datetimeProvider; + private readonly ISearchIndex _searchIndex; private readonly ObjectPool _persistenceStorePool; private readonly ObjectPool _executorPool; protected override QueueType Queue => QueueType.Workflow; - public WorkflowConsumer(IPooledObjectPolicy persistencePoolPolicy, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IPooledObjectPolicy executorPoolPolicy, IDateTimeProvider datetimeProvider, WorkflowOptions options) + public WorkflowConsumer(IPooledObjectPolicy persistencePoolPolicy, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IPooledObjectPolicy executorPoolPolicy, IDateTimeProvider datetimeProvider, ISearchIndex searchIndex, WorkflowOptions options) : base(queueProvider, loggerFactory, options) { _persistenceStorePool = new DefaultObjectPool(persistencePoolPolicy); _executorPool = new DefaultObjectPool(executorPoolPolicy); _lockProvider = lockProvider; + _searchIndex = searchIndex; _datetimeProvider = datetimeProvider; } @@ -50,6 +52,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { _executorPool.Return(executor); await persistenceStore.PersistWorkflow(workflow); + await _searchIndex.IndexWorkflow(workflow); } } } diff --git a/src/WorkflowCore/Services/DefaultProviders/NullSearchIndex.cs b/src/WorkflowCore/Services/DefaultProviders/NullSearchIndex.cs new file mode 100644 index 000000000..db66ccf57 --- /dev/null +++ b/src/WorkflowCore/Services/DefaultProviders/NullSearchIndex.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.Search; + +namespace WorkflowCore.Services +{ + public class NullSearchIndex : ISearchIndex + { + public Task IndexWorkflow(WorkflowInstance workflow) + { + return Task.CompletedTask; + } + + public Task> Search(string terms, int skip, int take, params SearchFilter[] filters) + { + throw new NotImplementedException(); + } + + public Task Start() + { + return Task.CompletedTask; + } + + public Task Stop() + { + return Task.CompletedTask; + } + } +} diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 708ae747f..f76c3f45d 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -19,9 +19,10 @@ public class WorkflowController : IWorkflowController private readonly IQueueProvider _queueProvider; private readonly IExecutionPointerFactory _pointerFactory; private readonly ILifeCycleEventHub _eventHub; + private readonly ISearchIndex _searchIndex; private readonly ILogger _logger; - public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory) + public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ISearchIndex searchIndex, ILoggerFactory loggerFactory) { _persistenceStore = persistenceStore; _lockProvider = lockProvider; @@ -29,6 +30,7 @@ public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLoc _queueProvider = queueProvider; _pointerFactory = pointerFactory; _eventHub = eventHub; + _searchIndex = searchIndex; _logger = loggerFactory.CreateLogger(); } @@ -82,6 +84,7 @@ public async Task StartWorkflow(string workflowId, int? version, string id = await _persistenceStore.CreateNewWorkflow(wf); await _queueProvider.QueueWork(id, QueueType.Workflow); + await _searchIndex.IndexWorkflow(wf); await _eventHub.PublishNotification(new WorkflowStarted() { EventTimeUtc = DateTime.UtcNow, @@ -124,6 +127,7 @@ public async Task SuspendWorkflow(string workflowId) { wf.Status = WorkflowStatus.Suspended; await _persistenceStore.PersistWorkflow(wf); + await _searchIndex.IndexWorkflow(wf); await _eventHub.PublishNotification(new WorkflowSuspended() { EventTimeUtc = DateTime.UtcNow, @@ -159,6 +163,7 @@ public async Task ResumeWorkflow(string workflowId) wf.Status = WorkflowStatus.Runnable; await _persistenceStore.PersistWorkflow(wf); requeue = true; + await _searchIndex.IndexWorkflow(wf); await _eventHub.PublishNotification(new WorkflowResumed() { EventTimeUtc = DateTime.UtcNow, @@ -192,6 +197,7 @@ public async Task TerminateWorkflow(string workflowId) var wf = await _persistenceStore.GetWorkflowInstance(workflowId); wf.Status = WorkflowStatus.Terminated; await _persistenceStore.PersistWorkflow(wf); + await _searchIndex.IndexWorkflow(wf); await _eventHub.PublishNotification(new WorkflowTerminated() { EventTimeUtc = DateTime.UtcNow, diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index 85bf079de..1230ac97f 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -32,8 +32,9 @@ public class WorkflowHost : IWorkflowHost, IDisposable public ILogger Logger { get; private set; } private readonly ILifeCycleEventHub _lifeCycleEventHub; + private readonly ISearchIndex _searchIndex; - public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, WorkflowOptions options, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IEnumerable backgroundTasks, IWorkflowController workflowController, ILifeCycleEventHub lifeCycleEventHub) + public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, WorkflowOptions options, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IEnumerable backgroundTasks, IWorkflowController workflowController, ILifeCycleEventHub lifeCycleEventHub, ISearchIndex searchIndex) { PersistenceStore = persistenceStore; QueueProvider = queueProvider; @@ -44,6 +45,7 @@ public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueP LockProvider = lockProvider; _backgroundTasks = backgroundTasks; _workflowController = workflowController; + _searchIndex = searchIndex; _lifeCycleEventHub = lifeCycleEventHub; _lifeCycleEventHub.Subscribe(HandleLifeCycleEvent); } @@ -82,6 +84,7 @@ public void Start() QueueProvider.Start().Wait(); LockProvider.Start().Wait(); _lifeCycleEventHub.Start().Wait(); + _searchIndex.Start().Wait(); Logger.LogInformation("Starting backgroud tasks"); @@ -101,6 +104,7 @@ public void Stop() QueueProvider.Stop().Wait(); LockProvider.Stop().Wait(); + _searchIndex.Stop().Wait(); _lifeCycleEventHub.Stop().Wait(); } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index d02b350f2..837d40ff6 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.7.0 - 1.7.0.0 - 1.7.0.0 + 1.8.0 + 1.8.0.0 + 1.8.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/Models/WorkflowSearchModel.cs b/src/providers/WorkflowCore.Providers.Elasticsearch/Models/WorkflowSearchModel.cs new file mode 100644 index 000000000..91c4838f7 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/Models/WorkflowSearchModel.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.Search; + +namespace WorkflowCore.Providers.Elasticsearch.Models +{ + public class WorkflowSearchModel + { + public string Id { get; set; } + + public string WorkflowDefinitionId { get; set; } + + public int Version { get; set; } + + public string Description { get; set; } + + public string Reference { get; set; } + + public DateTime? NextExecutionUtc { get; set; } + + public string Status { get; set; } + + public Dictionary Data { get; set; } = new Dictionary(); + + public IEnumerable DataTokens { get; set; } + + public DateTime CreateTime { get; set; } + + public DateTime? CompleteTime { get; set; } + + public ICollection WaitingSteps { get; set; } = new HashSet(); + + public ICollection SleepingSteps { get; set; } = new HashSet(); + + public ICollection FailedSteps { get; set; } = new HashSet(); + + public WorkflowSearchResult ToSearchResult() + { + var result = new WorkflowSearchResult() + { + Id = Id, + CompleteTime = CompleteTime, + CreateTime = CreateTime, + Description = Description, + NextExecutionUtc = NextExecutionUtc, + Reference = Reference, + Status = (WorkflowStatus) Enum.Parse(typeof(WorkflowStatus), Status, true), + Version = Version, + WorkflowDefinitionId = WorkflowDefinitionId, + FailedSteps = FailedSteps, + SleepingSteps = SleepingSteps, + WaitingSteps = WaitingSteps + }; + + if (Data.Count > 0) + result.Data = Data.First().Value; + + return result; + } + + public static WorkflowSearchModel FromWorkflowInstance(WorkflowInstance workflow) + { + var result = new WorkflowSearchModel(); + + result.Id = workflow.Id; + result.WorkflowDefinitionId = workflow.WorkflowDefinitionId; + result.Description = workflow.Description; + result.Reference = workflow.Reference; + + if (workflow.Data != null) + result.Data.Add(workflow.Data.GetType().FullName, workflow.Data); + + result.CompleteTime = workflow.CompleteTime; + result.CreateTime = workflow.CreateTime; + result.Version = workflow.Version; + result.Status = workflow.Status.ToString(); + + if (workflow.NextExecution.HasValue) + result.NextExecutionUtc = new DateTime(workflow.NextExecution.Value); + + if (workflow.Data is ISearchable) + result.DataTokens = (workflow.Data as ISearchable).GetSearchTokens(); + + foreach (var ep in workflow.ExecutionPointers) + { + if (ep.Status == PointerStatus.Sleeping) + { + result.SleepingSteps.Add(new StepInfo() + { + StepId = ep.StepId, + Name = ep.StepName + }); + } + + if (ep.Status == PointerStatus.WaitingForEvent) + { + result.WaitingSteps.Add(new StepInfo() + { + StepId = ep.StepId, + Name = ep.StepName + }); + } + + if (ep.Status == PointerStatus.Failed) + { + result.FailedSteps.Add(new StepInfo() + { + StepId = ep.StepId, + Name = ep.StepName + }); + } + } + + return result; + } + + } + + public class TypedWorkflowSearchModel : WorkflowSearchModel + { + public new Dictionary Data { get; set; } + } +} diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/README.md b/src/providers/WorkflowCore.Providers.Elasticsearch/README.md new file mode 100644 index 000000000..c75aa34d8 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/README.md @@ -0,0 +1,132 @@ +# Elasticsearch plugin for Workflow Core + +A search index plugin for Workflow Core backed by Elasticsearch, enabling you to index your workflows and search against the data and state of them. + +## Installing + +Install the NuGet package "WorkflowCore.Providers.Elasticsearch" + +Using Nuget package console +``` +PM> Install-Package WorkflowCore.Providers.Elasticsearch +``` + +Using .NET CLI +``` +dotnet add package WorkflowCore.Providers.Elasticsearch +``` + + +## Configuration + +Use the `.UseElasticsearch` extension method on `IServiceCollection` when building your service provider + +```C# +using Nest; +... +services.AddWorkflow(cfg => +{ + ... + cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://localhost:9200")), "index_name"); +}); +``` + +## Usage + +Inject the `ISearchIndex` service into your code and use the `Search` method. + +``` +Search(string terms, int skip, int take, params SearchFilter[] filters) +``` + +#### terms + +A whitespace separated string of search terms, an empty string will match everything. +This will do a full text search on the following default fields + * Reference + * Description + * Status + * Workflow Definition + + In addition you can search data within your own custom data object if it implements `ISearchable` + + ```c# + using WorkflowCore.Interfaces; + ... + public class MyData : ISearchable +{ + public string StrValue1 { get; set; } + public string StrValue2 { get; set; } + + public IEnumerable GetSearchTokens() + { + return new List() + { + StrValue1, + StrValue2 + }; + } +} + ``` + + ##### Examples + + Search all fields for "puppies" + ```c# + searchIndex.Search("puppies", 0, 10); + ``` + +#### skip & take + +Use `skip` and `take` to page your search results. Where `skip` is the result number to start from and `take` is the page size. + +#### filters + +You can also supply a list of filters to apply to the search, these can be applied to both the standard fields as well as any field within your custom data objects. +There is no need to implement `ISearchable` on your data object in order to use filters against it. + +The following filter types are available + * ScalarFilter + * DateRangeFilter + * NumericRangeFilter + * StatusFilter + + These exist in the `WorkflowCore.Models.Search` namespace. + + ##### Examples + + Filtering by reference + ```c# + using WorkflowCore.Models.Search; + ... + + searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Reference, "My Reference")); + ``` + + Filtering by workflows started after a date + ```c# + searchIndex.Search("", 0, 10, DateRangeFilter.After(x => x.CreateTime, startDate)); + ``` + + Filtering by workflows completed within a period + ```c# + searchIndex.Search("", 0, 10, DateRangeFilter.Between(x => x.CompleteTime, startDate, endDate)); + ``` + + Filtering by workflows in a state + ```c# + searchIndex.Search("", 0, 10, StatusFilter.Equals(WorkflowStatus.Complete)); + ``` + + Filtering against your own custom data class + ```c# + + class MyData + { + public string Value1 { get; set; } + public int Value2 { get; set; } + } + + searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Value1, "blue moon")); + searchIndex.Search("", 0, 10, NumericRangeFilter.LessThan(x => x.Value2, 5)) + ``` diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Elasticsearch/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..0fc834630 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/ServiceCollectionExtensions.cs @@ -0,0 +1,17 @@ +using System; +using Microsoft.Extensions.Logging; +using Nest; +using WorkflowCore.Models; +using WorkflowCore.Providers.Elasticsearch.Services; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + public static WorkflowOptions UseElasticsearch(this WorkflowOptions options, ConnectionSettings settings, string indexName) + { + options.UseSearchIndex(sp => new ElasticsearchIndexer(settings, indexName, sp.GetService())); + return options; + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs b/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs new file mode 100644 index 000000000..2c3634f38 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs @@ -0,0 +1,141 @@ +using Microsoft.Extensions.Logging; +using Nest; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.Search; +using WorkflowCore.Providers.Elasticsearch.Models; + +namespace WorkflowCore.Providers.Elasticsearch.Services +{ + public class ElasticsearchIndexer : ISearchIndex + { + private readonly ConnectionSettings _settings; + private readonly string _indexName; + private readonly ILogger _logger; + private IElasticClient _client; + + public ElasticsearchIndexer(ConnectionSettings settings, string indexName, ILoggerFactory loggerFactory) + { + _settings = settings; + _indexName = indexName.ToLower(); + _logger = loggerFactory.CreateLogger(GetType()); + } + + public async Task IndexWorkflow(WorkflowInstance workflow) + { + if (_client == null) + throw new InvalidOperationException("Not started"); + + try + { + var denormModel = WorkflowSearchModel.FromWorkflowInstance(workflow); + + var result = await _client.IndexAsync(denormModel, idx => idx + .Index(_indexName) + ); + + if (!result.ApiCall.Success) + { + _logger.LogError(default(EventId), result.ApiCall.OriginalException, $"Failed to index workflow {workflow.Id}"); + } + } + catch (Exception ex) + { + _logger.LogError(default(EventId), ex, $"Failed to index workflow {workflow.Id}"); + } + } + + public async Task> Search(string terms, int skip, int take, params SearchFilter[] filters) + { + if (_client == null) + throw new InvalidOperationException("Not started"); + + var result = await _client.SearchAsync(s => s + .Index(_indexName) + .Skip(skip) + .Take(take) + .MinScore(!string.IsNullOrEmpty(terms) ? 0.1 : 0) + .Query(query => query + .Bool(b => b + .Filter(BuildFilterQuery(filters)) + .Should( + should => should.Match(t => t.Field(f => f.Reference).Query(terms).Boost(1.2)), + should => should.Match(t => t.Field(f => f.DataTokens).Query(terms).Boost(1.1)), + should => should.Match(t => t.Field(f => f.WorkflowDefinitionId).Query(terms).Boost(0.9)), + should => should.Match(t => t.Field(f => f.Status).Query(terms).Boost(0.9)), + should => should.Match(t => t.Field(f => f.Description).Query(terms)) + ) + ) + ) + ); + + return new Page + { + Total = result.Total, + Data = result.Hits.Select(x => x.Source).Select(x => x.ToSearchResult()).ToList() + }; + } + + public async Task Start() + { + _client = new ElasticClient(_settings); + var nodeInfo = await _client.NodesInfoAsync(); + if (nodeInfo.Nodes.Values.Any(x => Convert.ToUInt32(x.Version.Split('.')[0]) < 6)) + throw new NotSupportedException("Elasticsearch verison 6 or greater is required"); + + var exists = await _client.IndexExistsAsync(_indexName); + if (!exists.Exists) + { + await _client.CreateIndexAsync(_indexName); + } + } + + public Task Stop() + { + _client = null; + return Task.CompletedTask; + } + + private List, QueryContainer>> BuildFilterQuery(SearchFilter[] filters) + { + var result = new List, QueryContainer>>(); + + foreach (var filter in filters) + { + var field = new Field(filter.Property); + if (filter.IsData) + { + Expression> dataExpr = x => x.Data[filter.DataType.FullName]; + var fieldExpr = Expression.Convert(filter.Property, typeof(Func)); + field = new Field(Expression.Invoke(fieldExpr, dataExpr)); + } + + switch (filter) + { + case ScalarFilter f: + result.Add(x => x.Match(t => t.Field(field).Query(Convert.ToString(f.Value)))); + break; + case DateRangeFilter f: + if (f.BeforeValue.HasValue) + result.Add(x => x.DateRange(t => t.Field(field).LessThan(f.BeforeValue))); + if (f.AfterValue.HasValue) + result.Add(x => x.DateRange(t => t.Field(field).GreaterThan(f.AfterValue))); + break; + case NumericRangeFilter f: + if (f.LessValue.HasValue) + result.Add(x => x.Range(t => t.Field(field).LessThan(f.LessValue))); + if (f.GreaterValue.HasValue) + result.Add(x => x.Range(t => t.Field(field).GreaterThan(f.GreaterValue))); + break; + } + } + + return result; + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj new file mode 100644 index 000000000..a1791afa6 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -0,0 +1,24 @@ + + + + netstandard2.0 + 1.8.0 + https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md + https://github.com/danielgerlag/workflow-core + https://github.com/danielgerlag/workflow-core.git + git + Daniel Gerlag + WorkflowCore + A search index plugin for Workflow Core backed by Elasticsearch, enabling you to index your workflows and search against the data and state of them. + + + + + + + + + + + + diff --git a/src/samples/WorkflowCore.Sample03/MyDataClass.cs b/src/samples/WorkflowCore.Sample03/MyDataClass.cs index de0f99f81..a3b51429c 100644 --- a/src/samples/WorkflowCore.Sample03/MyDataClass.cs +++ b/src/samples/WorkflowCore.Sample03/MyDataClass.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading.Tasks; +using WorkflowCore.Interface; namespace WorkflowCore.Sample03 { @@ -12,5 +14,6 @@ public class MyDataClass public int Value2 { get; set; } public int Value3 { get; set; } + } } diff --git a/src/samples/WorkflowCore.Sample04/EventSampleWorkflow.cs b/src/samples/WorkflowCore.Sample04/EventSampleWorkflow.cs index 20fd48f69..d07a5ac03 100644 --- a/src/samples/WorkflowCore.Sample04/EventSampleWorkflow.cs +++ b/src/samples/WorkflowCore.Sample04/EventSampleWorkflow.cs @@ -19,9 +19,9 @@ public void Build(IWorkflowBuilder builder) builder .StartWith(context => ExecutionResult.Next()) .WaitFor("MyEvent", (data, context) => context.Workflow.Id, data => DateTime.Now) - .Output(data => data.StrValue, step => step.EventData) + .Output(data => data.Value1, step => step.EventData) .Then() - .Input(step => step.Message, data => "The data from the event is " + data.StrValue) + .Input(step => step.Message, data => "The data from the event is " + data.Value1) .Then(context => Console.WriteLine("workflow complete")); } } diff --git a/src/samples/WorkflowCore.Sample04/MyDataClass.cs b/src/samples/WorkflowCore.Sample04/MyDataClass.cs index dda008f2c..0863891a2 100644 --- a/src/samples/WorkflowCore.Sample04/MyDataClass.cs +++ b/src/samples/WorkflowCore.Sample04/MyDataClass.cs @@ -7,6 +7,6 @@ namespace WorkflowCore.Sample04 { public class MyDataClass { - public string StrValue { get; set; } + public string Value1 { get; set; } } } diff --git a/src/samples/WorkflowCore.Sample07/web.config b/src/samples/WorkflowCore.Sample07/web.config index dc0514fca..b56a4065f 100644 --- a/src/samples/WorkflowCore.Sample07/web.config +++ b/src/samples/WorkflowCore.Sample07/web.config @@ -1,14 +1,14 @@  - - - + - + + + - + \ No newline at end of file diff --git a/test/ScratchPad/Program.cs b/test/ScratchPad/Program.cs index 6f11d9a39..91b4eb21a 100644 --- a/test/ScratchPad/Program.cs +++ b/test/ScratchPad/Program.cs @@ -11,6 +11,8 @@ using Amazon; using Amazon.DynamoDBv2; using Amazon.Runtime; +using Nest; +using WorkflowCore.Models.Search; namespace ScratchPad { @@ -18,36 +20,35 @@ public class Program { public static void Main(string[] args) { - //var s = typeof(HelloWorld).AssemblyQualifiedName; - - IServiceProvider serviceProvider = ConfigureServices(); //start the workflow host var host = serviceProvider.GetService(); - var persistence = serviceProvider.GetService(); + var searchIndex = serviceProvider.GetService(); - var wf = new WorkflowInstance() - { - Description = "test", - CreateTime = DateTime.UtcNow, - Status = WorkflowStatus.Terminated, - Version = 1, - WorkflowDefinitionId = "def" - }; - var id = persistence.CreateNewWorkflow(wf).Result; + host.RegisterWorkflow(); + host.RegisterWorkflow(); - //var loader = serviceProvider.GetService(); - //var str = ScratchPad.Properties.Resources.HelloWorld; //Encoding.UTF8.GetString(ScratchPad.Properties.Resources.HelloWorld); - //loader.LoadDefinition(str); - //host.RegisterWorkflow(); - //host.Start(); + host.Start(); + var data1 = new WorkflowCore.Sample03.MyDataClass() { Value1 = 2, Value2 = 3 }; + host.StartWorkflow("PassingDataWorkflow", data1, "quick dog").Wait(); - //host.StartWorkflow("HelloWorld", 1, new MyDataClass() { Value3 = "hi there" }); + var data2 = new WorkflowCore.Sample04.MyDataClass() { Value1 = "test" }; + host.StartWorkflow("EventSampleWorkflow", data2, "alt1 boom").Wait(); + var searchResult1 = searchIndex.Search("dog", 0, 10).Result; + var searchResult2 = searchIndex.Search("quick dog", 0, 10).Result; + var searchResult3 = searchIndex.Search("fast", 0, 10).Result; + var searchResult4 = searchIndex.Search("alt1", 0, 10).Result; + var searchResult5 = searchIndex.Search("dogs", 0, 10).Result; + var searchResult6 = searchIndex.Search("test", 0, 10).Result; + var searchResult7 = searchIndex.Search("", 0, 10).Result; + var searchResult8 = searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Reference, "quick dog")).Result; + var searchResult9 = searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Value1, 2)).Result; + Console.ReadLine(); - //host.Stop(); + host.Stop(); } private static IServiceProvider ConfigureServices() @@ -59,14 +60,14 @@ private static IServiceProvider ConfigureServices() //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); services.AddWorkflow(cfg => { - var ddbConfig = new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }; - cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), ddbConfig, "sample31"); + //var ddbConfig = new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }; + //cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), ddbConfig, "elastic"); + cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://localhost:9200")), "workflows"); //cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); //cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); }); - - services.AddTransient(); + services.AddTransient(); var serviceProvider = services.BuildServiceProvider(); @@ -77,62 +78,6 @@ private static IServiceProvider ConfigureServices() } } - - public class HelloWorld : StepBody - { - public override ExecutionResult Run(IStepExecutionContext context) - { - Console.WriteLine("Hello world"); - return ExecutionResult.Next(); - } - } - public class GoodbyeWorld : StepBody - { - public override ExecutionResult Run(IStepExecutionContext context) - { - Console.WriteLine("Goodbye world"); - return ExecutionResult.Next(); - } - } - - public class Throw : StepBody - { - public override ExecutionResult Run(IStepExecutionContext context) - { - Console.WriteLine("throwing..."); - throw new Exception("up"); - } - } - - public class PrintMessage : StepBody - { - public string Message { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - Console.WriteLine(Message); - return ExecutionResult.Next(); - } - } - - public class GenerateMessage : StepBody - { - public string Message { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - Message = "Generated message"; - return ExecutionResult.Next(); - } - } - - public class MyDataClass - { - public int Value1 { get; set; } - - public int Value2 { get; set; } - - public string Value3 { get; set; } - } + } diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 09dc7c647..6a1d90749 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -14,6 +14,11 @@ + + + + + diff --git a/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs b/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs new file mode 100644 index 000000000..8c834f82f --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using FluentAssertions; +using FluentAssertions.Collections; +using FluentAssertions.Equivalency; +using FluentAssertions.Common; +using Xunit; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.Search; + +namespace WorkflowCore.IntegrationTests +{ + public abstract class SearchIndexTests + { + protected abstract ISearchIndex CreateService(); + protected ISearchIndex Subject { get; set; } + + protected SearchIndexTests() + { + Subject = CreateService(); + Subject.Start().Wait(); + + foreach (var item in BuildTestData()) + Subject.IndexWorkflow(item).Wait(); + } + + protected IEnumerable BuildTestData() + { + var result = new List(); + + result.Add(new WorkflowInstance() + { + Id = "1", + CreateTime = new DateTime(2010, 1, 1), + Status = WorkflowStatus.Runnable, + Reference = "ref1" + }); + + result.Add(new WorkflowInstance() + { + Id = "2", + CreateTime = new DateTime(2020, 1, 1), + Status = WorkflowStatus.Runnable, + Reference = "ref2", + Data = new DataObject() + { + Value3 = 7 + } + }); + + result.Add(new WorkflowInstance() + { + Id = "3", + CreateTime = new DateTime(2010, 1, 1), + Status = WorkflowStatus.Complete, + Reference = "ref3", + Data = new DataObject() + { + Value3 = 5, + Value1 = "quick fox", + Value2 = "lazy dog" + } + }); + + result.Add(new WorkflowInstance() + { + Id = "4", + CreateTime = new DateTime(2010, 1, 1), + Status = WorkflowStatus.Complete, + Reference = "ref4", + Data = new AltDataObject() + { + Value1 = 9, + Value2 = new DateTime(2000, 1, 1) + } + }); + + return result; + } + + + [Fact] + public async void should_search_on_reference() + { + var result1 = await Subject.Search("ref1", 0, 10); + var result2 = await Subject.Search("ref2", 0, 10); + + result1.Data.Should().Contain(x => x.Id == "1"); + result1.Data.Should().NotContain(x => x.Id == "2"); + result1.Data.Should().NotContain(x => x.Id == "3"); + + result2.Data.Should().NotContain(x => x.Id == "1"); + result2.Data.Should().Contain(x => x.Id == "2"); + result2.Data.Should().NotContain(x => x.Id == "3"); + } + + [Fact] + public async void should_search_on_custom_data() + { + var result = await Subject.Search("dog fox", 0, 10); + + result.Data.Should().NotContain(x => x.Id == "1"); + result.Data.Should().NotContain(x => x.Id == "2"); + result.Data.Should().Contain(x => x.Id == "3"); + } + + [Fact] + public async void should_filter_on_custom_data() + { + var result = await Subject.Search(null, 0, 10, ScalarFilter.Equals(x => x.Value3, 7)); + + result.Data.Should().NotContain(x => x.Id == "1"); + result.Data.Should().Contain(x => x.Id == "2"); + result.Data.Should().NotContain(x => x.Id == "3"); + } + + [Fact] + public async void should_filter_on_alt_custom_data_with_conflicting_names() + { + var result1 = await Subject.Search(null, 0, 10, ScalarFilter.Equals(x => x.Value1, 9)); + var result2 = await Subject.Search(null, 0, 10, DateRangeFilter.Between(x => x.Value2, new DateTime(1999, 12, 31), new DateTime(2000, 1, 2))); + + result1.Data.Should().Contain(x => x.Id == "4"); + result2.Data.Should().Contain(x => x.Id == "4"); + } + + [Fact] + public async void should_filter_on_reference() + { + var result = await Subject.Search(null, 0, 10, ScalarFilter.Equals(x => x.Reference, "ref2")); + + result.Data.Should().NotContain(x => x.Id == "1"); + result.Data.Should().Contain(x => x.Id == "2"); + result.Data.Should().NotContain(x => x.Id == "3"); + } + + [Fact] + public async void should_filter_on_status() + { + var result = await Subject.Search(null, 0, 10, StatusFilter.Equals(WorkflowStatus.Runnable)); + + result.Data.Should().NotContain(x => x.Status != WorkflowStatus.Runnable); + result.Data.Should().Contain(x => x.Status == WorkflowStatus.Runnable); + } + + [Fact] + public async void should_filter_on_date_range() + { + var start = new DateTime(2000, 1, 1); + var end = new DateTime(2015, 1, 1); + var result = await Subject.Search(null, 0, 10, DateRangeFilter.Between(x => x.CreateTime, start, end)); + + result.Data.Should().NotContain(x => x.CreateTime < start || x.CreateTime > end); + result.Data.Should().Contain(x => x.CreateTime > start && x.CreateTime < end); + } + + class DataObject : ISearchable + { + public string Value1 { get; set; } + public string Value2 { get; set; } + + public int Value3 { get; set; } + + public IEnumerable GetSearchTokens() + { + return new List() + { + Value1, + Value2 + }; + } + } + + class AltDataObject + { + public int Value1 { get; set; } + public DateTime Value2 { get; set; } + } + } +} diff --git a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs new file mode 100644 index 000000000..e10dd5aa9 --- /dev/null +++ b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using Docker.Testify; +using Nest; +using Xunit; + +namespace WorkflowCore.Tests.Elasticsearch +{ + public class ElasticsearchDockerSetup : DockerSetup + { + public static string ConnectionString { get; set; } + + public override string ImageName => @"elasticsearch"; + public override string ImageTag => "6.5.4"; + public override int InternalPort => 9200; + + public override void PublishConnectionInfo() + { + ConnectionString = $"http://localhost:{ExternalPort}"; + } + + public override bool TestReady() + { + try + { + var client = new ElasticClient(new ConnectionSettings(new Uri($"http://localhost:{ExternalPort}"))); + var ping = client.Ping(); + return ping.IsValid; + } + catch + { + return false; + } + + } + } + + [CollectionDefinition("Elasticsearch collection")] + public class DynamoDbCollection : ICollectionFixture + { + } +} diff --git a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchIndexerTests.cs b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchIndexerTests.cs new file mode 100644 index 000000000..322191e0c --- /dev/null +++ b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchIndexerTests.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.Extensions.Logging; +using Nest; +using WorkflowCore.IntegrationTests; +using WorkflowCore.Interface; +using WorkflowCore.Providers.Elasticsearch.Services; +using Xunit; + +namespace WorkflowCore.Tests.Elasticsearch +{ + [Collection("Elasticsearch collection")] + public class ElasticsearchIndexerTests : SearchIndexTests + { + ElasticsearchDockerSetup _dockerSetup; + + public ElasticsearchIndexerTests(ElasticsearchDockerSetup dockerSetup) + { + _dockerSetup = dockerSetup; + } + + protected override ISearchIndex CreateService() + { + var settings = new ConnectionSettings(new Uri(ElasticsearchDockerSetup.ConnectionString)); + return new ElasticsearchIndexer(settings, "workflowcore.tests", new LoggerFactory()); + } + } +} diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj new file mode 100644 index 000000000..173c6ee1b --- /dev/null +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp2.1 + + false + + + + + + + + + + + + + + + + From 3d441f71b88d7326cd298129d9a83ee0c370ec2a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 19 Jan 2019 08:27:13 -0800 Subject: [PATCH 090/462] release notes --- ReleaseNotes/next.md | 41 ------------------- WorkflowCore.sln | 4 +- .../WorkflowCore.Testing.csproj | 4 +- 3 files changed, 4 insertions(+), 45 deletions(-) delete mode 100644 ReleaseNotes/next.md diff --git a/ReleaseNotes/next.md b/ReleaseNotes/next.md deleted file mode 100644 index 41fe49dae..000000000 --- a/ReleaseNotes/next.md +++ /dev/null @@ -1,41 +0,0 @@ - -## Action Inputs / Outputs - -Added the action Input & Output overloads on the fluent step builder. - -```c# -Input(Action action); -``` - -This will allow one to manipulate properties on the step before it executes and properties on the data object after it executes, for example - -```c# -Input((step, data) => step.Value1 = data.Value1) -``` - -```c# -.Output((step, data) => data["Value3"] = step.Output) -``` - -```c# -.Output((step, data) => data.MyCollection.Add(step.Output)) -``` - -## Breaking changes - -The existing ability to assign values to entries in dictionaries or dynamic objects on `.Output` was problematic, -since it broke the ability to pass collections on the Output mappings. - - -```c# -.Output(data => data["Value3"], step => step.Output) -``` - -This feature has been removed, and it is advised to use the action Output API instead, for example - - -```c# -.Output((step, data) => data["Value3"] = step.Output) -``` - -This functionality remains intact for JSON defined workflows. \ No newline at end of file diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 053f46bcf..779cb2e06 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -95,7 +95,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.6.8.md = ReleaseNotes\1.6.8.md ReleaseNotes\1.6.9.md = ReleaseNotes\1.6.9.md ReleaseNotes\1.7.0.md = ReleaseNotes\1.7.0.md - ReleaseNotes\next.md = ReleaseNotes\next.md + ReleaseNotes\1.8.0.md = ReleaseNotes\1.8.0.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" @@ -128,7 +128,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Providers.Redi EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Providers.Elasticsearch", "src\providers\WorkflowCore.Providers.Elasticsearch\WorkflowCore.Providers.Elasticsearch.csproj", "{F6348170-B695-4D97-BAE6-4F0F643F3BEF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.Elasticsearch", "test\WorkflowCore.Tests.Elasticsearch\WorkflowCore.Tests.Elasticsearch.csproj", "{44644716-0CE8-4837-B189-AB65AE2106AA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.Elasticsearch", "test\WorkflowCore.Tests.Elasticsearch\WorkflowCore.Tests.Elasticsearch.csproj", "{44644716-0CE8-4837-B189-AB65AE2106AA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 63c11078d..9d866621b 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,8 +2,8 @@ netstandard2.0 - 1.7.0 - 1.7.0.0 + 1.8.0 + 1.8.0.0 1.7.0.0 Facilitates testing of workflows built on Workflow-Core From 4580fce21bc28323ca4fc32e55ecf2ba6c89211e Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 19 Jan 2019 08:28:23 -0800 Subject: [PATCH 091/462] release notes --- ReleaseNotes/1.8.0.md | 160 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 ReleaseNotes/1.8.0.md diff --git a/ReleaseNotes/1.8.0.md b/ReleaseNotes/1.8.0.md new file mode 100644 index 000000000..a4222eba8 --- /dev/null +++ b/ReleaseNotes/1.8.0.md @@ -0,0 +1,160 @@ + +## Elasticsearch plugin for Workflow Core + +A search index plugin for Workflow Core backed by Elasticsearch, enabling you to index your workflows and search against the data and state of them. + +### Configuration + +Use the `.UseElasticsearch` extension method on `IServiceCollection` when building your service provider + +```C# +using Nest; +... +services.AddWorkflow(cfg => +{ + ... + cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://localhost:9200")), "index_name"); +}); +``` + +### Usage + +Inject the `ISearchIndex` service into your code and use the `Search` method. + +``` +Search(string terms, int skip, int take, params SearchFilter[] filters) +``` + +#### terms + +A whitespace separated string of search terms, an empty string will match everything. +This will do a full text search on the following default fields + * Reference + * Description + * Status + * Workflow Definition + + In addition you can search data within your own custom data object if it implements `ISearchable` + + ```c# + using WorkflowCore.Interfaces; + ... + public class MyData : ISearchable +{ + public string StrValue1 { get; set; } + public string StrValue2 { get; set; } + + public IEnumerable GetSearchTokens() + { + return new List() + { + StrValue1, + StrValue2 + }; + } +} + ``` + + ##### Examples + + Search all fields for "puppies" + ```c# + searchIndex.Search("puppies", 0, 10); + ``` + +#### skip & take + +Use `skip` and `take` to page your search results. Where `skip` is the result number to start from and `take` is the page size. + +#### filters + +You can also supply a list of filters to apply to the search, these can be applied to both the standard fields as well as any field within your custom data objects. +There is no need to implement `ISearchable` on your data object in order to use filters against it. + +The following filter types are available + * ScalarFilter + * DateRangeFilter + * NumericRangeFilter + * StatusFilter + + These exist in the `WorkflowCore.Models.Search` namespace. + + ##### Examples + + Filtering by reference + ```c# + using WorkflowCore.Models.Search; + ... + + searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Reference, "My Reference")); + ``` + + Filtering by workflows started after a date + ```c# + searchIndex.Search("", 0, 10, DateRangeFilter.After(x => x.CreateTime, startDate)); + ``` + + Filtering by workflows completed within a period + ```c# + searchIndex.Search("", 0, 10, DateRangeFilter.Between(x => x.CompleteTime, startDate, endDate)); + ``` + + Filtering by workflows in a state + ```c# + searchIndex.Search("", 0, 10, StatusFilter.Equals(WorkflowStatus.Complete)); + ``` + + Filtering against your own custom data class + ```c# + + class MyData + { + public string Value1 { get; set; } + public int Value2 { get; set; } + } + + searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Value1, "blue moon")); + searchIndex.Search("", 0, 10, NumericRangeFilter.LessThan(x => x.Value2, 5)) + ``` + + +## Action Inputs / Outputs + +Added the action Input & Output overloads on the fluent step builder. + +```c# +Input(Action action); +``` + +This will allow one to manipulate properties on the step before it executes and properties on the data object after it executes, for example + +```c# +Input((step, data) => step.Value1 = data.Value1) +``` + +```c# +.Output((step, data) => data["Value3"] = step.Output) +``` + +```c# +.Output((step, data) => data.MyCollection.Add(step.Output)) +``` + +## Breaking changes + +The existing ability to assign values to entries in dictionaries or dynamic objects on `.Output` was problematic, +since it broke the ability to pass collections on the Output mappings. + + +```c# +.Output(data => data["Value3"], step => step.Output) +``` + +This feature has been removed, and it is advised to use the action Output API instead, for example + + +```c# +.Output((step, data) => data["Value3"] = step.Output) +``` + +This functionality remains intact for JSON defined workflows. \ No newline at end of file From e56e8059e1366573b6a2da93f2578c2607a02861 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 20 Jan 2019 11:41:08 -0800 Subject: [PATCH 092/462] AWS Kinesis provider for event hubs (#246) --- .../IDynamoDbProvisioner.cs | 2 +- .../Interface/IKinesisStreamConsumer.cs | 13 ++ .../Interface/IKinesisTracker.cs | 13 ++ .../WorkflowCore.Providers.AWS/README.md | 19 ++- .../ServiceCollectionExtensions.cs | 11 ++ .../Services/DynamoDbProvisioner.cs | 2 +- .../Services/DynamoLockProvider.cs | 2 +- .../Services/DynamoPersistenceProvider.cs | 2 +- .../Services/KinesisProvider.cs | 141 ++++++++++++++++++ .../Services/KinesisStreamConsumer.cs | 140 +++++++++++++++++ .../Services/KinesisTracker.cs | 116 ++++++++++++++ .../WorkflowCore.Providers.AWS.csproj | 9 +- .../WorkflowCore.Sample04.csproj | 2 +- test/ScratchPad/ScratchPad.csproj | 2 +- .../WorkflowCore.Tests.DynamoDB.csproj | 2 +- 15 files changed, 463 insertions(+), 13 deletions(-) rename src/providers/WorkflowCore.Providers.AWS/{Services => Interface}/IDynamoDbProvisioner.cs (72%) create mode 100644 src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisStreamConsumer.cs create mode 100644 src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs create mode 100644 src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs create mode 100644 src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs create mode 100644 src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/IDynamoDbProvisioner.cs b/src/providers/WorkflowCore.Providers.AWS/Interface/IDynamoDbProvisioner.cs similarity index 72% rename from src/providers/WorkflowCore.Providers.AWS/Services/IDynamoDbProvisioner.cs rename to src/providers/WorkflowCore.Providers.AWS/Interface/IDynamoDbProvisioner.cs index 25498e4ae..0ec886ce3 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/IDynamoDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Interface/IDynamoDbProvisioner.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; -namespace WorkflowCore.Providers.AWS.Services +namespace WorkflowCore.Providers.AWS.Interface { public interface IDynamoDbProvisioner { diff --git a/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisStreamConsumer.cs b/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisStreamConsumer.cs new file mode 100644 index 000000000..41e48b013 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisStreamConsumer.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Amazon.Kinesis.Model; + +namespace WorkflowCore.Providers.AWS.Interface +{ + public interface IKinesisStreamConsumer + { + Task Subscribe(string appName, string stream, Action action); + } +} diff --git a/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs b/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs new file mode 100644 index 000000000..4e1d371b5 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace WorkflowCore.Providers.AWS.Interface +{ + public interface IKinesisTracker + { + Task GetNextShardIterator(string app, string stream, string shard); + Task IncrementShardIterator(string app, string stream, string shard, string iterator); + } +} diff --git a/src/providers/WorkflowCore.Providers.AWS/README.md b/src/providers/WorkflowCore.Providers.AWS/README.md index 77ea0e7b1..b0717fc36 100644 --- a/src/providers/WorkflowCore.Providers.AWS/README.md +++ b/src/providers/WorkflowCore.Providers.AWS/README.md @@ -3,6 +3,7 @@ * Provides persistence for [Workflow Core](../../README.md) using DynamoDB. * Provides Queueing support on [Workflow Core](../../README.md) using AWS Simple Queue Service. * Provides Distributed locking support on [Workflow Core](../../README.md) using DynamoDB. +* Provides event hub support on [Workflow Core](../../README.md) backed by AWS Kinesis. This makes it possible to have a cluster of nodes processing your workflows. @@ -14,7 +15,7 @@ Install the NuGet package "WorkflowCore.Providers.AWS" PM> Install-Package WorkflowCore.Providers.AWS ``` -## Usage +## Usage (Persistence, Queueing and distributed locking) Use the `IServiceCollection` extension methods when building your service provider * .UseAwsDynamoPersistence @@ -31,4 +32,18 @@ services.AddWorkflow(cfg => ``` If any AWS resources do not exists, they will be automatcially created. By default, all DynamoDB tables and indexes will be provisioned with a throughput of 1, you can modify these values from the AWS console. -You may also specify a prefix for the dynamo table names. \ No newline at end of file +You may also specify a prefix for the dynamo table names. + + +## Usage (Kinesis) + +Use the the `.UseAwsKinesis` extension method on `IServiceCollection` when building your service provider + +```C# +services.AddWorkflow(cfg => +{ + cfg.UseAwsKinesis(new EnvironmentVariablesAWSCredentials(), RegionEndpoint.USWest2, "app-name", "stream-name"); +}); +``` +The Kinesis provider will also create a DynamoDB table to track the postion in each shard of the Kinesis stream. +A shard position will be tracked for each app name that you connect with. \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs index 84ed7fe8b..6c32f1571 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs @@ -1,9 +1,12 @@ using System; +using Amazon; using Amazon.DynamoDBv2; using Amazon.Runtime; using Amazon.SQS; using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; using WorkflowCore.Models; +using WorkflowCore.Providers.AWS.Interface; using WorkflowCore.Providers.AWS.Services; namespace Microsoft.Extensions.DependencyInjection @@ -28,5 +31,13 @@ public static WorkflowOptions UseAwsDynamoPersistence(this WorkflowOptions optio options.UsePersistence(sp => new DynamoPersistenceProvider(credentials, config, sp.GetService(), tablePrefix, sp.GetService())); return options; } + + public static WorkflowOptions UseAwsKinesis(this WorkflowOptions options, AWSCredentials credentials, RegionEndpoint region, string appName, string streamName) + { + options.Services.AddTransient(sp => new KinesisTracker(credentials, region, "workflowcore_kinesis", sp.GetService())); + options.Services.AddTransient(sp => new KinesisStreamConsumer(credentials, region, sp.GetService(), sp.GetService(), sp.GetService())); + options.UseEventHub(sp => new KinesisProvider(credentials, region, appName, streamName, sp.GetService(), sp.GetService())); + return options; + } } } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs index 8dd2fcfa9..59f019892 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using WorkflowCore.Interface; +using WorkflowCore.Providers.AWS.Interface; namespace WorkflowCore.Providers.AWS.Services { diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs index 18bde2c61..7c6b63435 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -145,7 +145,7 @@ private async void SendHeartbeat() { try { - foreach (var item in _localLocks) + foreach (var item in _localLocks.ToArray()) { var req = new PutItemRequest { diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index d63fb07c9..d824e05e8 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using Amazon.Util; +using WorkflowCore.Providers.AWS.Interface; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs new file mode 100644 index 000000000..7db21aab7 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Amazon; +using Amazon.Kinesis; +using Amazon.Kinesis.Model; +using Amazon.Runtime; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using WorkflowCore.Interface; +using WorkflowCore.Models.LifeCycleEvents; +using WorkflowCore.Providers.AWS.Interface; + +namespace WorkflowCore.Providers.AWS.Services +{ + public class KinesisProvider : ILifeCycleEventHub + { + private readonly ILogger _logger; + private Queue> _deferredSubscribers = new Queue>(); + private readonly string _streamName; + private readonly string _appName; + private readonly JsonSerializer _serializer; + private readonly IKinesisStreamConsumer _consumer; + private readonly AmazonKinesisClient _client; + private readonly int _defaultShardCount = 1; + private bool _started = false; + + public KinesisProvider(AWSCredentials credentials, RegionEndpoint region, string appName, string streamName, IKinesisStreamConsumer consumer, ILoggerFactory logFactory) + { + _logger = logFactory.CreateLogger(GetType()); + _appName = appName; + _streamName = streamName; + _consumer = consumer; + _serializer = new JsonSerializer(); + _serializer.TypeNameHandling = TypeNameHandling.All; + _client = new AmazonKinesisClient(credentials, region); + } + + public async Task PublishNotification(LifeCycleEvent evt) + { + using (var stream = new MemoryStream()) + { + var writer = new StreamWriter(stream); + _serializer.Serialize(writer, evt); + writer.Flush(); + + var response = await _client.PutRecordAsync(new PutRecordRequest() + { + StreamName = _streamName, + PartitionKey = evt.WorkflowInstanceId, + Data = stream + }); + + //if (response.HttpStatusCode != System.Net.HttpStatusCode.OK) + //{ + // _logger.LogWarning($"Failed to send event to Kinesis {response.HttpStatusCode}"); + //} + } + } + + public void Subscribe(Action action) + { + if (_started) + { + _consumer.Subscribe(_appName, _streamName, record => Consume(record, action)); + } + else + { + _deferredSubscribers.Enqueue(action); + } + } + + public async Task Start() + { + await EnsureStream(); + _started = true; + while (_deferredSubscribers.Count > 0) + { + var action = _deferredSubscribers.Dequeue(); + await _consumer.Subscribe(_appName, _streamName, record => Consume(record, action)); + } + } + + public Task Stop() + { + _started = false; + return Task.CompletedTask; + } + + private async Task EnsureStream() + { + try + { + await _client.DescribeStreamSummaryAsync(new DescribeStreamSummaryRequest() + { + StreamName = _streamName + }); + } + catch (ResourceNotFoundException) + { + await CreateStream(); + } + } + + private async Task CreateStream() + { + await _client.CreateStreamAsync(new CreateStreamRequest() + { + StreamName = _streamName, + ShardCount = _defaultShardCount + }); + + var i = 0; + while (i < 20) + { + i++; + await Task.Delay(3000); + var poll = await _client.DescribeStreamSummaryAsync(new DescribeStreamSummaryRequest() + { + StreamName = _streamName + }); + + if (poll.StreamDescriptionSummary.StreamStatus == StreamStatus.ACTIVE) + return poll.StreamDescriptionSummary.StreamARN; + } + + throw new TimeoutException(); + } + + private void Consume(Record record, Action action) + { + using (var strm = new StreamReader(record.Data)) + { + var evt = _serializer.Deserialize(new JsonTextReader(strm)); + action(evt as LifeCycleEvent); + } + } + } +} diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs new file mode 100644 index 000000000..849dc071d --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Amazon; +using Amazon.Kinesis; +using Amazon.Kinesis.Model; +using Amazon.Runtime; +using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; +using WorkflowCore.Providers.AWS.Interface; + +namespace WorkflowCore.Providers.AWS.Services +{ + public class KinesisStreamConsumer : IKinesisStreamConsumer, IDisposable + { + private readonly ILogger _logger; + private readonly IKinesisTracker _tracker; + private readonly IDistributedLockProvider _lockManager; + private readonly AmazonKinesisClient _client; + private readonly CancellationTokenSource _cancelToken = new CancellationTokenSource(); + private readonly Task _processTask; + private readonly int _batchSize = 100; + private ICollection _subscribers = new HashSet(); + + public KinesisStreamConsumer(AWSCredentials credentials, RegionEndpoint region, IKinesisTracker tracker, IDistributedLockProvider lockManager, ILoggerFactory logFactory) + { + _logger = logFactory.CreateLogger(GetType()); + _tracker = tracker; + _lockManager = lockManager; + _client = new AmazonKinesisClient(credentials, region); + _processTask = new Task(Process); + _processTask.Start(); + } + + public async Task Subscribe(string appName, string stream, Action action) + { + var shards = await _client.ListShardsAsync(new ListShardsRequest() + { + StreamName = stream + }); + + foreach (var shard in shards.Shards) + { + _subscribers.Add(new ShardSubscription() + { + AppName = appName, + Stream = stream, + Shard = shard, + Action = action + }); + } + } + + private async void Process() + { + while (!_cancelToken.IsCancellationRequested) + { + try + { + var todo = _subscribers.Where(x => x.Snooze < DateTime.Now).ToList(); + foreach (var sub in todo) + { + if (!await _lockManager.AcquireLock($"{sub.AppName}.{sub.Stream}.{sub.Shard.ShardId}", + _cancelToken.Token)) + continue; + + try + { + var iterator = await _tracker.GetNextShardIterator(sub.AppName, sub.Stream, sub.Shard.ShardId); + + if (iterator == null) + { + var iterResp = await _client.GetShardIteratorAsync(new GetShardIteratorRequest() + { + ShardId = sub.Shard.ShardId, + StreamName = sub.Stream, + ShardIteratorType = ShardIteratorType.AFTER_SEQUENCE_NUMBER, + StartingSequenceNumber = sub.Shard.SequenceNumberRange.StartingSequenceNumber + }); + iterator = iterResp.ShardIterator; + } + + var records = await _client.GetRecordsAsync(new GetRecordsRequest() + { + ShardIterator = iterator, + Limit = _batchSize + }); + + if (records.Records.Count == 0) + sub.Snooze = DateTime.Now.AddSeconds(5); + + foreach (var rec in records.Records) + { + try + { + sub.Action(rec); + } + catch (Exception ex) + { + _logger.LogError(default(EventId), ex, ex.Message); + } + } + + await _tracker.IncrementShardIterator(sub.AppName, sub.Stream, sub.Shard.ShardId, records.NextShardIterator); + } + finally + { + await _lockManager.ReleaseLock($"{sub.AppName}.{sub.Stream}.{sub.Shard.ShardId}"); + } + } + + if (todo.Count == 0) + await Task.Delay(2000); + } + catch (Exception ex) + { + _logger.LogError(default(EventId), ex, ex.Message); + } + } + } + + + public void Dispose() + { + _cancelToken.Cancel(); + _processTask.Wait(5000); + } + + class ShardSubscription + { + public string AppName { get; set; } + public string Stream { get; set; } + public Shard Shard { get; set; } + public Action Action { get; set; } + public DateTime Snooze { get; set; } = DateTime.Now; + } + } +} diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs new file mode 100644 index 000000000..53fc33073 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Amazon; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using Amazon.Runtime; +using Microsoft.Extensions.Logging; +using WorkflowCore.Providers.AWS.Interface; + +namespace WorkflowCore.Providers.AWS.Services +{ + public class KinesisTracker : IKinesisTracker + { + private readonly ILogger _logger; + private readonly AmazonDynamoDBClient _client; + private readonly string _tableName; + private bool _tableConfirmed = false; + + public KinesisTracker(AWSCredentials credentials, RegionEndpoint region, string tableName, ILoggerFactory logFactory) + { + _logger = logFactory.CreateLogger(GetType()); + _client = new AmazonDynamoDBClient(credentials, region); + _tableName = tableName; + } + + public async Task GetNextShardIterator(string app, string stream, string shard) + { + if (!_tableConfirmed) + await EnsureTable(); + + var response = await _client.GetItemAsync(new GetItemRequest() + { + TableName = _tableName, + Key = new Dictionary + { + { "id", new AttributeValue(FormatId(app, stream, shard)) } + } + }); + + if (!response.Item.ContainsKey("next_iterator")) + return null; + + return response.Item["next_iterator"].S; + } + + public async Task IncrementShardIterator(string app, string stream, string shard, string iterator) + { + if (!_tableConfirmed) + await EnsureTable(); + + await _client.PutItemAsync(new PutItemRequest() + { + TableName = _tableName, + Item = new Dictionary + { + { "id", new AttributeValue(FormatId(app, stream, shard)) }, + { "next_iterator", new AttributeValue(iterator) } + } + }); + } + + private async Task EnsureTable() + { + try + { + var poll = await _client.DescribeTableAsync(_tableName); + _tableConfirmed = true; + } + catch (ResourceNotFoundException) + { + await CreateTable(); + } + } + + private async Task CreateTable() + { + var createRequest = new CreateTableRequest(_tableName, new List() + { + new KeySchemaElement("id", KeyType.HASH) + }) + { + AttributeDefinitions = new List() + { + new AttributeDefinition("id", ScalarAttributeType.S) + }, + BillingMode = BillingMode.PAY_PER_REQUEST + }; + + await _client.CreateTableAsync(createRequest); + + int i = 0; + while (i < 20) + { + try + { + i++; + await Task.Delay(1000); + var poll = await _client.DescribeTableAsync(_tableName); + if (poll.Table.TableStatus == TableStatus.ACTIVE) + { + _tableConfirmed = true; + return; + } + } + catch (ResourceNotFoundException) + { + } + } + } + + private string FormatId(string app, string stream, string shard) => $"{app}.{stream}.{shard}"; + } +} diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index abcc32771..7aa437903 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,13 +11,14 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 1.7.0 - 1.7.0.0 + 1.8.0 + 1.8.0.0 - - + + + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index caf8f9550..bcbe56b99 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -23,7 +23,7 @@ - + diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 6a1d90749..2d05c5ed1 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -23,7 +23,7 @@ - + diff --git a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index d72f927a8..2296962c3 100644 --- a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -7,7 +7,7 @@ - + From fe9804057e006afff89025efb00afc86af622371 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 22 Jan 2019 19:48:32 -0800 Subject: [PATCH 093/462] web sample --- .dockerignore | 10 ++ src/samples/WebApiSample/.dockerignore | 10 ++ src/samples/WebApiSample/WebApiSample.sln | 31 ++++++ .../Controllers/EventsController.cs | 32 ++++++ .../Controllers/WorkflowsController.cs | 103 ++++++++++++++++++ .../WebApiSample/WebApiSample/Dockerfile | 19 ++++ .../WebApiSample/Dockerfile.original | 19 ++++ .../WebApiSample/WebApiSample/Program.cs | 24 ++++ .../Properties/launchSettings.json | 35 ++++++ .../WebApiSample/WebApiSample/Startup.cs | 51 +++++++++ .../WebApiSample/WebApiSample.csproj | 24 ++++ .../WebApiSample/Workflows/TestWorkflow.cs | 30 +++++ .../WebApiSample/appsettings.Development.json | 9 ++ .../WebApiSample/appsettings.json | 8 ++ .../WebApiSample/docker-compose.dcproj | 18 +++ .../WebApiSample/docker-compose.override.yml | 8 ++ src/samples/WebApiSample/docker-compose.yml | 18 +++ 17 files changed, 449 insertions(+) create mode 100644 .dockerignore create mode 100644 src/samples/WebApiSample/.dockerignore create mode 100644 src/samples/WebApiSample/WebApiSample.sln create mode 100644 src/samples/WebApiSample/WebApiSample/Controllers/EventsController.cs create mode 100644 src/samples/WebApiSample/WebApiSample/Controllers/WorkflowsController.cs create mode 100644 src/samples/WebApiSample/WebApiSample/Dockerfile create mode 100644 src/samples/WebApiSample/WebApiSample/Dockerfile.original create mode 100644 src/samples/WebApiSample/WebApiSample/Program.cs create mode 100644 src/samples/WebApiSample/WebApiSample/Properties/launchSettings.json create mode 100644 src/samples/WebApiSample/WebApiSample/Startup.cs create mode 100644 src/samples/WebApiSample/WebApiSample/WebApiSample.csproj create mode 100644 src/samples/WebApiSample/WebApiSample/Workflows/TestWorkflow.cs create mode 100644 src/samples/WebApiSample/WebApiSample/appsettings.Development.json create mode 100644 src/samples/WebApiSample/WebApiSample/appsettings.json create mode 100644 src/samples/WebApiSample/docker-compose.dcproj create mode 100644 src/samples/WebApiSample/docker-compose.override.yml create mode 100644 src/samples/WebApiSample/docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..43e8ab1e3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj diff --git a/src/samples/WebApiSample/.dockerignore b/src/samples/WebApiSample/.dockerignore new file mode 100644 index 000000000..43e8ab1e3 --- /dev/null +++ b/src/samples/WebApiSample/.dockerignore @@ -0,0 +1,10 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj diff --git a/src/samples/WebApiSample/WebApiSample.sln b/src/samples/WebApiSample/WebApiSample.sln new file mode 100644 index 000000000..c37bd7b35 --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.168 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiSample", "WebApiSample\WebApiSample.csproj", "{14489389-A65D-4993-8DE2-F51701A5AF60}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{2D5D708D-7EA1-48A9-ABF0-64CCC6026435}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {14489389-A65D-4993-8DE2-F51701A5AF60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14489389-A65D-4993-8DE2-F51701A5AF60}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14489389-A65D-4993-8DE2-F51701A5AF60}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14489389-A65D-4993-8DE2-F51701A5AF60}.Release|Any CPU.Build.0 = Release|Any CPU + {2D5D708D-7EA1-48A9-ABF0-64CCC6026435}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D5D708D-7EA1-48A9-ABF0-64CCC6026435}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D5D708D-7EA1-48A9-ABF0-64CCC6026435}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D5D708D-7EA1-48A9-ABF0-64CCC6026435}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EEC447B3-434E-4573-A280-D4A6CC652274} + EndGlobalSection +EndGlobal diff --git a/src/samples/WebApiSample/WebApiSample/Controllers/EventsController.cs b/src/samples/WebApiSample/WebApiSample/Controllers/EventsController.cs new file mode 100644 index 000000000..faa70a2e3 --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/Controllers/EventsController.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WebApiSample.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class EventsController : Controller + { + private readonly IWorkflowController _workflowService; + + public EventsController(IWorkflowController workflowService) + { + _workflowService = workflowService; + } + + [HttpPost("{eventName}/{eventKey}")] + public async Task Post(string eventName, string eventKey, [FromBody]object eventData) + { + await _workflowService.PublishEvent(eventName, eventKey, eventData); + return Ok(); + } + + } +} diff --git a/src/samples/WebApiSample/WebApiSample/Controllers/WorkflowsController.cs b/src/samples/WebApiSample/WebApiSample/Controllers/WorkflowsController.cs new file mode 100644 index 000000000..694d9b05b --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/Controllers/WorkflowsController.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.Search; + +namespace WebApiSample.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class WorkflowsController : Controller + { + private readonly IWorkflowController _workflowService; + private readonly IWorkflowRegistry _registry; + private readonly IPersistenceProvider _workflowStore; + private readonly ISearchIndex _searchService; + + public WorkflowsController(IWorkflowController workflowService, ISearchIndex searchService, IWorkflowRegistry registry, IPersistenceProvider workflowStore) + { + _workflowService = workflowService; + _workflowStore = workflowStore; + _registry = registry; + _searchService = searchService; + } + + + [HttpGet] + public async Task Get(string terms, WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take = 10) + { + var filters = new List(); + + if (status.HasValue) + filters.Add(StatusFilter.Equals(status.Value)); + + if (createdFrom.HasValue) + filters.Add(DateRangeFilter.After(x => x.CreateTime, createdFrom.Value)); + + if (createdTo.HasValue) + filters.Add(DateRangeFilter.Before(x => x.CreateTime, createdTo.Value)); + + if (!string.IsNullOrEmpty(type)) + filters.Add(ScalarFilter.Equals(x => x.WorkflowDefinitionId, type)); + + var result = await _searchService.Search(terms, skip, take, filters.ToArray()); + + return Json(result); + } + + [HttpGet("{id}")] + public async Task Get(string id) + { + var result = await _workflowStore.GetWorkflowInstance(id); + return Json(result); + } + + [HttpPost("{id}")] + [HttpPost("{id}/{version}")] + public async Task Post(string id, int? version, string reference, [FromBody]JObject data) + { + string workflowId = null; + var def = _registry.GetDefinition(id, version); + if (def == null) + return BadRequest(String.Format("Workflow defintion {0} for version {1} not found", id, version)); + + if ((data != null) && (def.DataType != null)) + { + var dataStr = JsonConvert.SerializeObject(data); + var dataObj = JsonConvert.DeserializeObject(dataStr, def.DataType); + workflowId = await _workflowService.StartWorkflow(id, version, dataObj, reference); + } + else + { + workflowId = await _workflowService.StartWorkflow(id, version, null, reference); + } + + return Ok(workflowId); + } + + [HttpPut("{id}/suspend")] + public Task Suspend(string id) + { + return _workflowService.SuspendWorkflow(id); + } + + [HttpPut("{id}/resume")] + public Task Resume(string id) + { + return _workflowService.ResumeWorkflow(id); + } + + [HttpDelete("{id}")] + public Task Terminate(string id) + { + return _workflowService.TerminateWorkflow(id); + } + } +} diff --git a/src/samples/WebApiSample/WebApiSample/Dockerfile b/src/samples/WebApiSample/WebApiSample/Dockerfile new file mode 100644 index 000000000..f3bec7528 --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/Dockerfile @@ -0,0 +1,19 @@ +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base +WORKDIR /app +EXPOSE 80 + +FROM microsoft/dotnet:2.1-sdk AS build +WORKDIR /src +COPY WebApiSample/WebApiSample.csproj WebApiSample/ +RUN dotnet restore WebApiSample/WebApiSample.csproj +COPY . . +WORKDIR /src/WebApiSample +RUN dotnet build WebApiSample.csproj -c Release -o /app + +FROM build AS publish +RUN dotnet publish WebApiSample.csproj -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "WebApiSample.dll"] diff --git a/src/samples/WebApiSample/WebApiSample/Dockerfile.original b/src/samples/WebApiSample/WebApiSample/Dockerfile.original new file mode 100644 index 000000000..8c834bb3b --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/Dockerfile.original @@ -0,0 +1,19 @@ +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base +WORKDIR /app +EXPOSE 80 + +FROM microsoft/dotnet:2.1-sdk AS build +WORKDIR /src +COPY ["WebApiSample/WebApiSample.csproj", "WebApiSample/"] +RUN dotnet restore "WebApiSample/WebApiSample.csproj" +COPY . . +WORKDIR "/src/WebApiSample" +RUN dotnet build "WebApiSample.csproj" -c Release -o /app + +FROM build AS publish +RUN dotnet publish "WebApiSample.csproj" -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "WebApiSample.dll"] \ No newline at end of file diff --git a/src/samples/WebApiSample/WebApiSample/Program.cs b/src/samples/WebApiSample/WebApiSample/Program.cs new file mode 100644 index 000000000..df2e76437 --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace WebApiSample +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} diff --git a/src/samples/WebApiSample/WebApiSample/Properties/launchSettings.json b/src/samples/WebApiSample/WebApiSample/Properties/launchSettings.json new file mode 100644 index 000000000..b5a3dd6fc --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/Properties/launchSettings.json @@ -0,0 +1,35 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:52884", + "sslPort": 0 + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "api/values", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WebApiSample": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "api/values", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:5000" + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/api/values" + } + } +} \ No newline at end of file diff --git a/src/samples/WebApiSample/WebApiSample/Startup.cs b/src/samples/WebApiSample/WebApiSample/Startup.cs new file mode 100644 index 000000000..d59257122 --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/Startup.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Nest; +using WebApiSample.Workflows; +using WorkflowCore.Interface; + +namespace WebApiSample +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); + services.AddWorkflow(cfg => + { + cfg.UseMongoDB(@"mongodb://mongo:27017", "workflow"); + cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://elastic:9200")), "workflows"); + }); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseMvc(); + + var host = app.ApplicationServices.GetService(); + host.RegisterWorkflow(); + host.Start(); + } + } +} diff --git a/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj b/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj new file mode 100644 index 000000000..938a7108e --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp2.1 + Linux + ..\docker-compose.dcproj + + + + + + + + + + + + + + + + + + diff --git a/src/samples/WebApiSample/WebApiSample/Workflows/TestWorkflow.cs b/src/samples/WebApiSample/WebApiSample/Workflows/TestWorkflow.cs new file mode 100644 index 000000000..df01ae156 --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/Workflows/TestWorkflow.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WebApiSample.Workflows +{ + public class TestWorkflow : IWorkflow + { + public string Id => "TestWorkflow"; + + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .WaitFor("MyEvent", (data, context) => context.Workflow.Id, data => DateTime.Now) + .Output(data => data.Value1, step => step.EventData) + .Then(context => Console.WriteLine("workflow complete")); + } + } + + public class MyDataClass + { + public string Value1 { get; set; } + } +} diff --git a/src/samples/WebApiSample/WebApiSample/appsettings.Development.json b/src/samples/WebApiSample/WebApiSample/appsettings.Development.json new file mode 100644 index 000000000..e203e9407 --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/src/samples/WebApiSample/WebApiSample/appsettings.json b/src/samples/WebApiSample/WebApiSample/appsettings.json new file mode 100644 index 000000000..def9159a7 --- /dev/null +++ b/src/samples/WebApiSample/WebApiSample/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/samples/WebApiSample/docker-compose.dcproj b/src/samples/WebApiSample/docker-compose.dcproj new file mode 100644 index 000000000..b3385d19e --- /dev/null +++ b/src/samples/WebApiSample/docker-compose.dcproj @@ -0,0 +1,18 @@ + + + + 2.1 + Linux + 2d5d708d-7ea1-48a9-abf0-64ccc6026435 + LaunchBrowser + {Scheme}://localhost:{ServicePort}/api/workflows + webapisample + + + + docker-compose.yml + + + + + \ No newline at end of file diff --git a/src/samples/WebApiSample/docker-compose.override.yml b/src/samples/WebApiSample/docker-compose.override.yml new file mode 100644 index 000000000..3dcfad473 --- /dev/null +++ b/src/samples/WebApiSample/docker-compose.override.yml @@ -0,0 +1,8 @@ +version: '3.4' + +services: + webapisample: + environment: + - ASPNETCORE_ENVIRONMENT=Development + ports: + - "80" diff --git a/src/samples/WebApiSample/docker-compose.yml b/src/samples/WebApiSample/docker-compose.yml new file mode 100644 index 000000000..4f46b919a --- /dev/null +++ b/src/samples/WebApiSample/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.4' + +services: + webapisample: + image: ${DOCKER_REGISTRY-}webapisample + build: + context: . + dockerfile: WebApiSample/Dockerfile + + elastic: + image: elasticsearch:6.5.4 + expose: + - 9200 + + mongo: + image: mongo:3.6 + expose: + - 27017 From 16c3e0fc1a6139f8af4570419332416e1e085442 Mon Sep 17 00:00:00 2001 From: Mario Andron Date: Wed, 23 Jan 2019 09:15:09 +0200 Subject: [PATCH 094/462] Enhanced to allow scoped dependecies to be injected into steps --- src/WorkflowCore/Services/WorkflowExecutor.cs | 85 ++++++++++--------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index a3e5c0c46..f1aa003a3 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -87,57 +87,60 @@ public async Task Execute(WorkflowInstance workflow) pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); } - _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); + using (var scope = _serviceProvider.CreateScope()) + { + _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); - IStepBody body = step.ConstructBody(_serviceProvider); + IStepBody body = step.ConstructBody(scope.ServiceProvider); - if (body == null) - { - _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); - pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); - wfResult.Errors.Add(new ExecutionError() + if (body == null) { - WorkflowId = workflow.Id, - ExecutionPointerId = pointer.Id, - ErrorTime = _datetimeProvider.Now.ToUniversalTime(), - Message = String.Format("Unable to construct step body {0}", step.BodyType.ToString()) - }); - continue; - } + _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); + pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); + wfResult.Errors.Add(new ExecutionError() + { + WorkflowId = workflow.Id, + ExecutionPointerId = pointer.Id, + ErrorTime = _datetimeProvider.Now.ToUniversalTime(), + Message = String.Format("Unable to construct step body {0}", step.BodyType.ToString()) + }); + continue; + } - IStepExecutionContext context = new StepExecutionContext() - { - Workflow = workflow, - Step = step, - PersistenceData = pointer.PersistenceData, - ExecutionPointer = pointer, - Item = pointer.ContextItem - }; + IStepExecutionContext context = new StepExecutionContext() + { + Workflow = workflow, + Step = step, + PersistenceData = pointer.PersistenceData, + ExecutionPointer = pointer, + Item = pointer.ContextItem + }; - foreach (var input in step.Inputs) - input.AssignInput(workflow.Data, body, context); + foreach (var input in step.Inputs) + input.AssignInput(workflow.Data, body, context); - switch (step.BeforeExecute(wfResult, context, pointer, body)) - { - case ExecutionPipelineDirective.Defer: - continue; - case ExecutionPipelineDirective.EndWorkflow: - workflow.Status = WorkflowStatus.Complete; - workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); - continue; - } + switch (step.BeforeExecute(wfResult, context, pointer, body)) + { + case ExecutionPipelineDirective.Defer: + continue; + case ExecutionPipelineDirective.EndWorkflow: + workflow.Status = WorkflowStatus.Complete; + workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); + continue; + } - var result = await body.RunAsync(context); + var result = await body.RunAsync(context); - if (result.Proceed) - { - foreach (var output in step.Outputs) - output.AssignOutput(workflow.Data, body, context); - } + if (result.Proceed) + { + foreach (var output in step.Outputs) + output.AssignOutput(workflow.Data, body, context); + } - _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); - step.AfterExecute(wfResult, context, result, pointer); + _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); + step.AfterExecute(wfResult, context, result, pointer); + } } catch (Exception ex) { From b25f3f0f16feaff55c65e1938433ece05ca2e9f2 Mon Sep 17 00:00:00 2001 From: Mario Andron Date: Wed, 23 Jan 2019 10:46:12 +0200 Subject: [PATCH 095/462] Fixed unit tests by adding a scope provider --- src/WorkflowCore/Interface/IScopeProvider.cs | 17 +++++++++++++ .../ServiceCollectionExtensions.cs | 1 + src/WorkflowCore/Services/ScopeProvider.cs | 25 +++++++++++++++++++ src/WorkflowCore/Services/WorkflowExecutor.cs | 6 +++-- .../Services/WorkflowExecutorFixture.cs | 8 +++++- 5 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/WorkflowCore/Interface/IScopeProvider.cs create mode 100644 src/WorkflowCore/Services/ScopeProvider.cs diff --git a/src/WorkflowCore/Interface/IScopeProvider.cs b/src/WorkflowCore/Interface/IScopeProvider.cs new file mode 100644 index 000000000..c69351731 --- /dev/null +++ b/src/WorkflowCore/Interface/IScopeProvider.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace WorkflowCore.Interface +{ + /// + /// The implemention of this interface will be responsible for + /// providing a new service scope for a DI container + /// + public interface IScopeProvider + { + /// + /// Create a new service scope + /// + /// + IServiceScope CreateScope(); + } +} \ No newline at end of file diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index aebae27a1..1d10c5742 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -46,6 +46,7 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(); services.AddSingleton(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/WorkflowCore/Services/ScopeProvider.cs b/src/WorkflowCore/Services/ScopeProvider.cs new file mode 100644 index 000000000..ad1ae98d2 --- /dev/null +++ b/src/WorkflowCore/Services/ScopeProvider.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using WorkflowCore.Interface; + +namespace WorkflowCore.Services +{ + /// + /// A concrete implementation for the IScopeProvider interface + /// Largely to get around the problems of unit testing an extension method (CreateScope()) + /// + public class ScopeProvider : IScopeProvider + { + private readonly IServiceProvider provider; + + public ScopeProvider(IServiceProvider provider) + { + this.provider = provider; + } + + public IServiceScope CreateScope() + { + return provider.CreateScope(); + } + } +} diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index f1aa003a3..45716b83d 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -14,6 +14,7 @@ public class WorkflowExecutor : IWorkflowExecutor { protected readonly IWorkflowRegistry _registry; protected readonly IServiceProvider _serviceProvider; + protected readonly IScopeProvider _scopeProvider; protected readonly IDateTimeProvider _datetimeProvider; protected readonly ILogger _logger; private readonly IExecutionResultProcessor _executionResultProcessor; @@ -23,9 +24,10 @@ public class WorkflowExecutor : IWorkflowExecutor private IWorkflowHost Host => _serviceProvider.GetService(); - public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, ICancellationProcessor cancellationProcessor, WorkflowOptions options, ILoggerFactory loggerFactory) + public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IScopeProvider scopeProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, ICancellationProcessor cancellationProcessor, WorkflowOptions options, ILoggerFactory loggerFactory) { _serviceProvider = serviceProvider; + _scopeProvider = scopeProvider; _registry = registry; _datetimeProvider = datetimeProvider; _publisher = publisher; @@ -87,7 +89,7 @@ public async Task Execute(WorkflowInstance workflow) pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); } - using (var scope = _serviceProvider.CreateScope()) + using (var scope = _scopeProvider.CreateScope()) { _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index d94ed3416..7597532d5 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -25,6 +25,7 @@ public class WorkflowExecutorFixture protected ILifeCycleEventPublisher EventHub; protected ICancellationProcessor CancellationProcessor; protected IServiceProvider ServiceProvider; + protected IScopeProvider ScopeProvider; protected IDateTimeProvider DateTimeProvider; protected WorkflowOptions Options; @@ -33,6 +34,7 @@ public WorkflowExecutorFixture() Host = A.Fake(); PersistenceProvider = A.Fake(); ServiceProvider = A.Fake(); + ScopeProvider = A.Fake(); Registry = A.Fake(); ResultProcesser = A.Fake(); EventHub = A.Fake(); @@ -41,13 +43,17 @@ public WorkflowExecutorFixture() Options = new WorkflowOptions(A.Fake()); + var scope = A.Fake(); + A.CallTo(() => ScopeProvider.CreateScope()).Returns(scope); + A.CallTo(() => scope.ServiceProvider).Returns(ServiceProvider); + A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); //config logging var loggerFactory = new LoggerFactory(); loggerFactory.AddConsole(LogLevel.Debug); - Subject = new WorkflowExecutor(Registry, ServiceProvider, DateTimeProvider, ResultProcesser, EventHub, CancellationProcessor, Options, loggerFactory); + Subject = new WorkflowExecutor(Registry, ServiceProvider, ScopeProvider, DateTimeProvider, ResultProcesser, EventHub, CancellationProcessor, Options, loggerFactory); } [Fact(DisplayName = "Should execute active step")] From 63d0f5af98d60e190919fcc421fd2c8242bf9e46 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 23 Jan 2019 07:54:21 -0800 Subject: [PATCH 096/462] wip --- .../Interface/IKinesisTracker.cs | 2 + .../Services/KinesisStreamConsumer.cs | 75 ++++++++++++++----- .../Services/KinesisTracker.cs | 47 +++++++++++- 3 files changed, 101 insertions(+), 23 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs b/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs index 4e1d371b5..3a4cee3a0 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs @@ -8,6 +8,8 @@ namespace WorkflowCore.Providers.AWS.Interface public interface IKinesisTracker { Task GetNextShardIterator(string app, string stream, string shard); + Task GetNextLastSequenceNumber(string app, string stream, string shard); Task IncrementShardIterator(string app, string stream, string shard, string iterator); + Task IncrementShardIteratorAndSequence(string app, string stream, string shard, string iterator, string sequence); } } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs index 849dc071d..c22e40fce 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs @@ -68,31 +68,16 @@ private async void Process() try { - var iterator = await _tracker.GetNextShardIterator(sub.AppName, sub.Stream, sub.Shard.ShardId); - - if (iterator == null) - { - var iterResp = await _client.GetShardIteratorAsync(new GetShardIteratorRequest() - { - ShardId = sub.Shard.ShardId, - StreamName = sub.Stream, - ShardIteratorType = ShardIteratorType.AFTER_SEQUENCE_NUMBER, - StartingSequenceNumber = sub.Shard.SequenceNumberRange.StartingSequenceNumber - }); - iterator = iterResp.ShardIterator; - } - - var records = await _client.GetRecordsAsync(new GetRecordsRequest() - { - ShardIterator = iterator, - Limit = _batchSize - }); + var records = await GetBatch(sub); if (records.Records.Count == 0) sub.Snooze = DateTime.Now.AddSeconds(5); + var lastSequence = string.Empty; + foreach (var rec in records.Records) { + lastSequence = rec.SequenceNumber; try { sub.Action(rec); @@ -103,7 +88,10 @@ private async void Process() } } - await _tracker.IncrementShardIterator(sub.AppName, sub.Stream, sub.Shard.ShardId, records.NextShardIterator); + if (lastSequence != string.Empty) + await _tracker.IncrementShardIteratorAndSequence(sub.AppName, sub.Stream, sub.Shard.ShardId, records.NextShardIterator, lastSequence); + else + await _tracker.IncrementShardIterator(sub.AppName, sub.Stream, sub.Shard.ShardId, records.NextShardIterator); } finally { @@ -121,6 +109,53 @@ private async void Process() } } + private async Task GetBatch(ShardSubscription sub) + { + var iterator = await _tracker.GetNextShardIterator(sub.AppName, sub.Stream, sub.Shard.ShardId); + + if (iterator == null) + { + var iterResp = await _client.GetShardIteratorAsync(new GetShardIteratorRequest() + { + ShardId = sub.Shard.ShardId, + StreamName = sub.Stream, + ShardIteratorType = ShardIteratorType.AT_SEQUENCE_NUMBER, + StartingSequenceNumber = sub.Shard.SequenceNumberRange.StartingSequenceNumber + }); + iterator = iterResp.ShardIterator; + } + + try + { + var result = await _client.GetRecordsAsync(new GetRecordsRequest() + { + ShardIterator = iterator, + Limit = _batchSize + }); + + return result; + } + catch (ExpiredIteratorException) + { + var lastSequence = await _tracker.GetNextLastSequenceNumber(sub.AppName, sub.Stream, sub.Shard.ShardId); + var iterResp = await _client.GetShardIteratorAsync(new GetShardIteratorRequest() + { + ShardId = sub.Shard.ShardId, + StreamName = sub.Stream, + ShardIteratorType = ShardIteratorType.AFTER_SEQUENCE_NUMBER, + StartingSequenceNumber = lastSequence + }); + iterator = iterResp.ShardIterator; + + var result = await _client.GetRecordsAsync(new GetRecordsRequest() + { + ShardIterator = iterator, + Limit = _batchSize + }); + + return result; + } + } public void Dispose() { diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs index 53fc33073..5f4abc2e1 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs @@ -46,7 +46,47 @@ public async Task GetNextShardIterator(string app, string stream, string return response.Item["next_iterator"].S; } + public async Task GetNextLastSequenceNumber(string app, string stream, string shard) + { + if (!_tableConfirmed) + await EnsureTable(); + + var response = await _client.GetItemAsync(new GetItemRequest() + { + TableName = _tableName, + Key = new Dictionary + { + { "id", new AttributeValue(FormatId(app, stream, shard)) } + } + }); + + if (!response.Item.ContainsKey("last_sequence")) + return null; + + return response.Item["last_sequence"].S; + } + public async Task IncrementShardIterator(string app, string stream, string shard, string iterator) + { + if (!_tableConfirmed) + await EnsureTable(); + + await _client.UpdateItemAsync(new UpdateItemRequest() + { + TableName = _tableName, + Key = new Dictionary + { + {"id", new AttributeValue(FormatId(app, stream, shard))} + }, + UpdateExpression = "SET next_iterator = :n", + ExpressionAttributeValues = new Dictionary() + { + { ":n" , new AttributeValue(iterator) } + } + }); + } + + public async Task IncrementShardIteratorAndSequence(string app, string stream, string shard, string iterator, string sequence) { if (!_tableConfirmed) await EnsureTable(); @@ -56,12 +96,13 @@ await _client.PutItemAsync(new PutItemRequest() TableName = _tableName, Item = new Dictionary { - { "id", new AttributeValue(FormatId(app, stream, shard)) }, - { "next_iterator", new AttributeValue(iterator) } + {"id", new AttributeValue(FormatId(app, stream, shard))}, + {"next_iterator", new AttributeValue(iterator)}, + {"last_sequence", new AttributeValue(sequence)} } }); } - + private async Task EnsureTable() { try From 9397958ad08b2c7ba1bdbb53a6f1d31ac03f6149 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 23 Jan 2019 20:24:45 -0800 Subject: [PATCH 097/462] bump version --- .../WorkflowCore.Providers.AWS.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 7aa437903..70c6896ef 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,8 +11,8 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 1.8.0 - 1.8.0.0 + 1.8.1 + 1.8.1.0 From 6ba5b710390064dcd821484c80e55d162126943a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 24 Jan 2019 19:38:31 -0800 Subject: [PATCH 098/462] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 3b143c39a..9de9b5c0e 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,6 @@ These are also available as separate Nuget packages. ## Extensions * [User (human) workflows](src/extensions/WorkflowCore.Users) -* [HTTP wrapper for Workflow Host Service](src/extensions/WorkflowCore.WebAPI) ## Samples @@ -163,7 +162,7 @@ These are also available as separate Nuget packages. * [Looping](src/samples/WorkflowCore.Sample02) -* [Exposing a REST API](src/samples/WorkflowCore.Sample07) +* [Exposing a REST API](src/samples/WebApiSample) * [Human(User) Workflow](src/samples/WorkflowCore.Sample08) From 7dea069d453ea92db5d1af91a14fcd8893f35aac Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 27 Jan 2019 07:28:46 -0800 Subject: [PATCH 099/462] Redis persistence provider (#253) --- WorkflowCore.sln | 7 + .../Services/DynamoDbProvisioner.cs | 2 +- .../Services/DynamoLockProvider.cs | 2 +- .../Services/DynamoPersistenceProvider.cs | 2 +- .../WorkflowCore.Providers.Redis/README.md | 7 +- .../ServiceCollectionExtensions.cs | 6 + .../Services/RedisPersistenceProvider.cs | 181 ++++++++++++++++++ .../WorkflowCore.Providers.Redis.csproj | 8 +- src/samples/WorkflowCore.Sample04/Program.cs | 1 + .../Scenarios/DynamoBasicScenario.cs | 2 +- .../Scenarios/DynamoCompensationScenario.cs | 2 +- .../Scenarios/DynamoDataScenario.cs | 2 +- .../Scenarios/DynamoDynamicDataScenario.cs | 2 +- .../Scenarios/DynamoEventScenario.cs | 2 +- .../Scenarios/DynamoForeachScenario.cs | 2 +- .../Scenarios/DynamoIfScenario.cs | 2 +- .../Scenarios/DynamoRetrySagaScenario.cs | 2 +- .../Scenarios/DynamoSagaScenario.cs | 2 +- .../Scenarios/DynamoWhileScenario.cs | 2 +- .../RedisDockerSetup.cs | 44 +++++ .../RedisPersistenceProviderFixture.cs | 37 ++++ .../Scenarios/RedisBasicScenario.cs | 16 ++ .../Scenarios/RedisDataScenario.cs | 16 ++ .../Scenarios/RedisEventScenario.cs | 16 ++ .../Scenarios/RedisForeachScenario.cs | 16 ++ .../Scenarios/RedisIfScenario.cs | 16 ++ .../Scenarios/RedisWhileScenario.cs | 16 ++ .../WorkflowCore.Tests.Redis.csproj | 24 +++ .../{ => Models}/MemberMapParameterTests.cs | 0 29 files changed, 417 insertions(+), 20 deletions(-) create mode 100644 src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs create mode 100644 test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs create mode 100644 test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs create mode 100644 test/WorkflowCore.Tests.Redis/Scenarios/RedisBasicScenario.cs create mode 100644 test/WorkflowCore.Tests.Redis/Scenarios/RedisDataScenario.cs create mode 100644 test/WorkflowCore.Tests.Redis/Scenarios/RedisEventScenario.cs create mode 100644 test/WorkflowCore.Tests.Redis/Scenarios/RedisForeachScenario.cs create mode 100644 test/WorkflowCore.Tests.Redis/Scenarios/RedisIfScenario.cs create mode 100644 test/WorkflowCore.Tests.Redis/Scenarios/RedisWhileScenario.cs create mode 100644 test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj rename test/WorkflowCore.UnitTests/{ => Models}/MemberMapParameterTests.cs (100%) diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 779cb2e06..1393240ae 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -130,6 +130,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Providers.Elas EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.Elasticsearch", "test\WorkflowCore.Tests.Elasticsearch\WorkflowCore.Tests.Elasticsearch.csproj", "{44644716-0CE8-4837-B189-AB65AE2106AA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.Redis", "test\WorkflowCore.Tests.Redis\WorkflowCore.Tests.Redis.csproj", "{78217204-B873-40B9-8875-E3925B2FBCEC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -328,6 +330,10 @@ Global {44644716-0CE8-4837-B189-AB65AE2106AA}.Debug|Any CPU.Build.0 = Debug|Any CPU {44644716-0CE8-4837-B189-AB65AE2106AA}.Release|Any CPU.ActiveCfg = Release|Any CPU {44644716-0CE8-4837-B189-AB65AE2106AA}.Release|Any CPU.Build.0 = Release|Any CPU + {78217204-B873-40B9-8875-E3925B2FBCEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78217204-B873-40B9-8875-E3925B2FBCEC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78217204-B873-40B9-8875-E3925B2FBCEC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78217204-B873-40B9-8875-E3925B2FBCEC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -384,6 +390,7 @@ Global {435C6263-C6F8-4E93-B417-D861E9C22E18} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {F6348170-B695-4D97-BAE6-4F0F643F3BEF} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {44644716-0CE8-4837-B189-AB65AE2106AA} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} + {78217204-B873-40B9-8875-E3925B2FBCEC} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs index 59f019892..6930e6322 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs @@ -13,7 +13,7 @@ namespace WorkflowCore.Providers.AWS.Services public class DynamoDbProvisioner : IDynamoDbProvisioner { private readonly ILogger _logger; - private readonly AmazonDynamoDBClient _client; + private readonly IAmazonDynamoDB _client; private readonly string _tablePrefix; public DynamoDbProvisioner(AWSCredentials credentials, AmazonDynamoDBConfig config, string tablePrefix, ILoggerFactory logFactory) diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs index 7c6b63435..ce1458b60 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -13,7 +13,7 @@ namespace WorkflowCore.Providers.AWS.Services public class DynamoLockProvider : IDistributedLockProvider { private readonly ILogger _logger; - private readonly AmazonDynamoDBClient _client; + private readonly IAmazonDynamoDB _client; private readonly string _tableName; private readonly string _nodeId; private readonly long _ttl = 30000; diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index d824e05e8..1a6c7bf81 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -15,7 +15,7 @@ namespace WorkflowCore.Providers.AWS.Services public class DynamoPersistenceProvider : IPersistenceProvider { private readonly ILogger _logger; - private readonly AmazonDynamoDBClient _client; + private readonly IAmazonDynamoDB _client; private readonly string _tablePrefix; private readonly IDynamoDbProvisioner _provisioner; diff --git a/src/providers/WorkflowCore.Providers.Redis/README.md b/src/providers/WorkflowCore.Providers.Redis/README.md index 86545e714..bad88ac9d 100644 --- a/src/providers/WorkflowCore.Providers.Redis/README.md +++ b/src/providers/WorkflowCore.Providers.Redis/README.md @@ -1,5 +1,6 @@ # Redis providers for Workflow Core +* Provides Persistence support on [Workflow Core](../../README.md) backed by Redis. * Provides Queueing support on [Workflow Core](../../README.md) backed by Redis. * Provides Distributed locking support on [Workflow Core](../../README.md) backed by Redis. * Provides event hub support on [Workflow Core](../../README.md) backed by Redis. @@ -23,6 +24,7 @@ dotnet add package WorkflowCore.Providers.Redis ## Usage Use the `IServiceCollection` extension methods when building your service provider +* .UseRedisPersistence * .UseRedisQueues * .UseRedisLocking * .UseRedisEventHub @@ -30,8 +32,9 @@ Use the `IServiceCollection` extension methods when building your service provid ```C# services.AddWorkflow(cfg => { + cfg.UseRedisPersistence("localhost:6379", "app-name"); cfg.UseRedisLocking("localhost:6379"); - cfg.UseRedisQueues("localhost:6379", "my-app"); - cfg.UseRedisEventHub("localhost:6379", "my-channel") + cfg.UseRedisQueues("localhost:6379", "app-name"); + cfg.UseRedisEventHub("localhost:6379", "channel-name") }); ``` diff --git a/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs index 1ce0a091d..a517690f2 100644 --- a/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs @@ -20,6 +20,12 @@ public static WorkflowOptions UseRedisLocking(this WorkflowOptions options, stri return options; } + public static WorkflowOptions UseRedisPersistence(this WorkflowOptions options, string connectionString, string prefix) + { + options.UsePersistence(sp => new RedisPersistenceProvider(connectionString, prefix, sp.GetService())); + return options; + } + public static WorkflowOptions UseRedisEventHub(this WorkflowOptions options, string connectionString, string channel) { options.UseEventHub(sp => new RedisLifeCycleEventHub(connectionString, channel, sp.GetService())); diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs new file mode 100644 index 000000000..57f64c4ef --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using StackExchange.Redis; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Providers.Redis.Services +{ + public class RedisPersistenceProvider : IPersistenceProvider + { + private readonly ILogger _logger; + private readonly string _connectionString; + private readonly string _prefix; + private const string WORKFLOW_SET = "workflows"; + private const string SUBSCRIPTION_SET = "subscriptions"; + private const string EVENT_SET = "events"; + private const string RUNNABLE_INDEX = "runnable"; + private const string EVENTSLUG_INDEX = "eventslug"; + private readonly IConnectionMultiplexer _multiplexer; + private readonly IDatabase _redis; + + private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + + public RedisPersistenceProvider(string connectionString, string prefix, ILoggerFactory logFactory) + { + _connectionString = connectionString; + _prefix = prefix; + _logger = logFactory.CreateLogger(GetType()); + _multiplexer = ConnectionMultiplexer.Connect(_connectionString); + _redis = _multiplexer.GetDatabase(); + } + + public async Task CreateNewWorkflow(WorkflowInstance workflow) + { + workflow.Id = Guid.NewGuid().ToString(); + await PersistWorkflow(workflow); + return workflow.Id; + } + + public async Task PersistWorkflow(WorkflowInstance workflow) + { + var str = JsonConvert.SerializeObject(workflow, _serializerSettings); + await _redis.HashSetAsync($"{_prefix}.{WORKFLOW_SET}", workflow.Id, str); + + if ((workflow.Status == WorkflowStatus.Runnable) && (workflow.NextExecution.HasValue)) + await _redis.SortedSetAddAsync($"{_prefix}.{WORKFLOW_SET}.{RUNNABLE_INDEX}", workflow.Id, workflow.NextExecution.Value); + else + await _redis.SortedSetRemoveAsync($"{_prefix}.{WORKFLOW_SET}.{RUNNABLE_INDEX}", workflow.Id); + } + + public async Task> GetRunnableInstances(DateTime asAt) + { + var result = new List(); + var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{WORKFLOW_SET}.{RUNNABLE_INDEX}", -1, DateTime.UtcNow.Ticks); + + foreach (var item in data) + result.Add(item); + + return result; + } + + public async Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, + int take) + { + throw new NotImplementedException(); + } + + public async Task GetWorkflowInstance(string Id) + { + var raw = await _redis.HashGetAsync($"{_prefix}.{WORKFLOW_SET}", Id); + return JsonConvert.DeserializeObject(raw, _serializerSettings); + } + + public async Task CreateEventSubscription(EventSubscription subscription) + { + subscription.Id = Guid.NewGuid().ToString(); + var str = JsonConvert.SerializeObject(subscription, _serializerSettings); + await _redis.HashSetAsync($"{_prefix}.{SUBSCRIPTION_SET}", subscription.Id, str); + await _redis.SortedSetAddAsync($"{_prefix}.{SUBSCRIPTION_SET}.{EVENTSLUG_INDEX}.{subscription.EventName}-{subscription.EventKey}", subscription.Id, subscription.SubscribeAsOf.Ticks); + + return subscription.Id; + } + + public async Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) + { + var result = new List(); + var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{SUBSCRIPTION_SET}.{EVENTSLUG_INDEX}.{eventName}-{eventKey}", -1, asOf.Ticks); + + foreach (var id in data) + { + var raw = await _redis.HashGetAsync($"{_prefix}.{SUBSCRIPTION_SET}", id); + if (raw.HasValue) + result.Add(JsonConvert.DeserializeObject(raw, _serializerSettings)); + } + + return result; + } + + public async Task TerminateSubscription(string eventSubscriptionId) + { + var existingRaw = await _redis.HashGetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId); + var existing = JsonConvert.DeserializeObject(existingRaw, _serializerSettings); + await _redis.HashDeleteAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId); + await _redis.SortedSetRemoveAsync($"{_prefix}.{SUBSCRIPTION_SET}.{EVENTSLUG_INDEX}.{existing.EventName}-{existing.EventKey}", eventSubscriptionId); + } + + public async Task CreateEvent(Event newEvent) + { + newEvent.Id = Guid.NewGuid().ToString(); + var str = JsonConvert.SerializeObject(newEvent, _serializerSettings); + await _redis.HashSetAsync($"{_prefix}.{EVENT_SET}", newEvent.Id, str); + await _redis.SortedSetAddAsync($"{_prefix}.{EVENT_SET}.{EVENTSLUG_INDEX}.{newEvent.EventName}-{newEvent.EventKey}", newEvent.Id, newEvent.EventTime.Ticks); + + if (newEvent.IsProcessed) + await _redis.SortedSetRemoveAsync($"{_prefix}.{EVENT_SET}.{RUNNABLE_INDEX}", newEvent.Id); + else + await _redis.SortedSetAddAsync($"{_prefix}.{EVENT_SET}.{RUNNABLE_INDEX}", newEvent.Id, newEvent.EventTime.Ticks); + + return newEvent.Id; + } + + public async Task GetEvent(string id) + { + var raw = await _redis.HashGetAsync($"{_prefix}.{EVENT_SET}", id); + return JsonConvert.DeserializeObject(raw, _serializerSettings); + } + + public async Task> GetRunnableEvents(DateTime asAt) + { + var result = new List(); + var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{EVENT_SET}.{RUNNABLE_INDEX}", -1, asAt.Ticks); + + foreach (var item in data) + result.Add(item); + + return result; + } + + public async Task> GetEvents(string eventName, string eventKey, DateTime asOf) + { + var result = new List(); + var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{EVENT_SET}.{EVENTSLUG_INDEX}.{eventName}-{eventKey}", asOf.Ticks); + + foreach (var id in data) + result.Add(id); + + return result; + } + + public async Task MarkEventProcessed(string id) + { + var evt = await GetEvent(id); + evt.IsProcessed = true; + var str = JsonConvert.SerializeObject(evt, _serializerSettings); + await _redis.HashSetAsync($"{_prefix}.{EVENT_SET}", evt.Id, str); + await _redis.SortedSetRemoveAsync($"{_prefix}.{EVENT_SET}.{RUNNABLE_INDEX}", id); + } + + public async Task MarkEventUnprocessed(string id) + { + var evt = await GetEvent(id); + evt.IsProcessed = false; + var str = JsonConvert.SerializeObject(evt, _serializerSettings); + await _redis.HashSetAsync($"{_prefix}.{EVENT_SET}", evt.Id, str); + await _redis.SortedSetAddAsync($"{_prefix}.{EVENT_SET}.{RUNNABLE_INDEX}", evt.Id, evt.EventTime.Ticks); + } + + public Task PersistErrors(IEnumerable errors) + { + return Task.CompletedTask; + } + + public void EnsureStoreExists() + { + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index 9f410a435..910b79be1 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,15 +2,13 @@ netstandard2.0 - 1.7.0 + 1.8.0 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git https://github.com/danielgerlag/workflow-core - Redis providers for Workflow Core - -- Provides Queueing support on Workflow Core -- Provides distributed locking support on Workflow Core + Redis providers for Workflow Core (Persistence, queueing, distributed locking and event hubs) + diff --git a/src/samples/WorkflowCore.Sample04/Program.cs b/src/samples/WorkflowCore.Sample04/Program.cs index ca8081d11..f977929cf 100644 --- a/src/samples/WorkflowCore.Sample04/Program.cs +++ b/src/samples/WorkflowCore.Sample04/Program.cs @@ -72,6 +72,7 @@ private static IServiceProvider ConfigureServices() //services.AddWorkflow(cfg => //{ + // cfg.UseRedisPersistence("localhost:6379", "sample4"); // cfg.UseRedisLocking("localhost:6379"); // cfg.UseRedisQueues("localhost:6379", "sample4"); // cfg.UseRedisEventHub("localhost:6379", "channel1"); diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs index dad0dd2ae..66a621c4f 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs @@ -8,7 +8,7 @@ using WorkflowCore.Tests.DynamoDB; using Xunit; -namespace WorkflowCore.Tests.MongoDB.Scenarios +namespace WorkflowCore.Tests.DynamoDB.Scenarios { [Collection("DynamoDb collection")] public class DynamoBasicScenario : BasicScenario diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs index 0c247d648..23ea7b32c 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs @@ -8,7 +8,7 @@ using WorkflowCore.Tests.DynamoDB; using Xunit; -namespace WorkflowCore.Tests.MongoDB.Scenarios +namespace WorkflowCore.Tests.DynamoDB.Scenarios { [Collection("DynamoDb collection")] public class DynamoCompensationScenario : CompensationScenario diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs index b6fffaa51..e0214203d 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs @@ -8,7 +8,7 @@ using WorkflowCore.Tests.DynamoDB; using Xunit; -namespace WorkflowCore.Tests.MongoDB.Scenarios +namespace WorkflowCore.Tests.DynamoDB.Scenarios { [Collection("DynamoDb collection")] public class DynamoDataScenario : DataIOScenario diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs index f84122ae0..d5d0ff5ac 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs @@ -8,7 +8,7 @@ using WorkflowCore.Tests.DynamoDB; using Xunit; -namespace WorkflowCore.Tests.MongoDB.Scenarios +namespace WorkflowCore.Tests.DynamoDB.Scenarios { [Collection("DynamoDb collection")] public class DynamoDynamicDataScenario : DynamicDataIOScenario diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs index 1999c35ad..887e39541 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs @@ -8,7 +8,7 @@ using WorkflowCore.Tests.DynamoDB; using Xunit; -namespace WorkflowCore.Tests.MongoDB.Scenarios +namespace WorkflowCore.Tests.DynamoDB.Scenarios { [Collection("DynamoDb collection")] public class DynamoEventScenario : EventScenario diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs index 5c7443a32..a8026da4b 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs @@ -8,7 +8,7 @@ using WorkflowCore.Tests.DynamoDB; using Xunit; -namespace WorkflowCore.Tests.MongoDB.Scenarios +namespace WorkflowCore.Tests.DynamoDB.Scenarios { [Collection("DynamoDb collection")] public class DynamoForeachScenario : ForeachScenario diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs index 7a2dc1d14..2b7ff3fac 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs @@ -8,7 +8,7 @@ using WorkflowCore.Tests.DynamoDB; using Xunit; -namespace WorkflowCore.Tests.MongoDB.Scenarios +namespace WorkflowCore.Tests.DynamoDB.Scenarios { [Collection("DynamoDb collection")] public class DynamoIfScenario : IfScenario diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs index f46266b99..a09dad420 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs @@ -8,7 +8,7 @@ using WorkflowCore.Tests.DynamoDB; using Xunit; -namespace WorkflowCore.Tests.MongoDB.Scenarios +namespace WorkflowCore.Tests.DynamoDB.Scenarios { [Collection("DynamoDb collection")] public class DynamoRetrySagaScenario : RetrySagaScenario diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs index 47f5063b9..9cb309614 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs @@ -8,7 +8,7 @@ using WorkflowCore.Tests.DynamoDB; using Xunit; -namespace WorkflowCore.Tests.MongoDB.Scenarios +namespace WorkflowCore.Tests.DynamoDB.Scenarios { [Collection("DynamoDb collection")] public class DynamoSagaScenario : SagaScenario diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs index ae11abbbd..50960652a 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs @@ -8,7 +8,7 @@ using WorkflowCore.Tests.DynamoDB; using Xunit; -namespace WorkflowCore.Tests.MongoDB.Scenarios +namespace WorkflowCore.Tests.DynamoDB.Scenarios { [Collection("DynamoDb collection")] public class DynamoWhileScenario : WhileScenario diff --git a/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs b/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs new file mode 100644 index 000000000..cfd3d11f2 --- /dev/null +++ b/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs @@ -0,0 +1,44 @@ +using Docker.DotNet; +using Docker.DotNet.Models; +using System; +using System.Collections.Generic; +using System.Net; +using Docker.Testify; +using StackExchange.Redis; +using Xunit; + +namespace WorkflowCore.Tests.Redis +{ + public class RedisDockerSetup : DockerSetup + { + public static string ConnectionString { get; set; } + + public override string ImageName => @"redis"; + public override int InternalPort => 6379; + + public override void PublishConnectionInfo() + { + ConnectionString = $"localhost:{ExternalPort}"; + } + + public override bool TestReady() + { + try + { + var multiplexer = ConnectionMultiplexer.Connect($"localhost:{ExternalPort}"); + return multiplexer.IsConnected; + } + catch + { + return false; + } + + } + } + + [CollectionDefinition("Redis collection")] + public class RedisCollection : ICollectionFixture + { + } + +} diff --git a/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs new file mode 100644 index 000000000..105e5900a --- /dev/null +++ b/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; +using WorkflowCore.Providers.Redis.Services; +using WorkflowCore.UnitTests; +using Xunit; + +namespace WorkflowCore.Tests.Redis +{ + [Collection("Redis collection")] + public class RedisPersistenceProviderFixture : BasePersistenceFixture + { + RedisDockerSetup _dockerSetup; + private IPersistenceProvider _subject; + + public RedisPersistenceProviderFixture(RedisDockerSetup dockerSetup) + { + _dockerSetup = dockerSetup; + } + + protected override IPersistenceProvider Subject + { + get + { + if (_subject == null) + { + var client = new RedisPersistenceProvider(RedisDockerSetup.ConnectionString, "test", new LoggerFactory()); + client.EnsureStoreExists(); + _subject = client; + } + return _subject; + } + } + } +} diff --git a/test/WorkflowCore.Tests.Redis/Scenarios/RedisBasicScenario.cs b/test/WorkflowCore.Tests.Redis/Scenarios/RedisBasicScenario.cs new file mode 100644 index 000000000..8a28ff189 --- /dev/null +++ b/test/WorkflowCore.Tests.Redis/Scenarios/RedisBasicScenario.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.Redis.Scenarios +{ + [Collection("Redis collection")] + public class RedisBasicScenario : BasicScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseRedisPersistence(RedisDockerSetup.ConnectionString, "scenario-")); + } + } +} diff --git a/test/WorkflowCore.Tests.Redis/Scenarios/RedisDataScenario.cs b/test/WorkflowCore.Tests.Redis/Scenarios/RedisDataScenario.cs new file mode 100644 index 000000000..1d79e240a --- /dev/null +++ b/test/WorkflowCore.Tests.Redis/Scenarios/RedisDataScenario.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.Redis.Scenarios +{ + [Collection("Redis collection")] + public class RedisDataScenario : DataIOScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseRedisPersistence(RedisDockerSetup.ConnectionString, "scenario-")); + } + } +} diff --git a/test/WorkflowCore.Tests.Redis/Scenarios/RedisEventScenario.cs b/test/WorkflowCore.Tests.Redis/Scenarios/RedisEventScenario.cs new file mode 100644 index 000000000..77a06ee3d --- /dev/null +++ b/test/WorkflowCore.Tests.Redis/Scenarios/RedisEventScenario.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.Redis.Scenarios +{ + [Collection("Redis collection")] + public class RedisEventScenario : DataIOScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseRedisPersistence(RedisDockerSetup.ConnectionString, "scenario-")); + } + } +} diff --git a/test/WorkflowCore.Tests.Redis/Scenarios/RedisForeachScenario.cs b/test/WorkflowCore.Tests.Redis/Scenarios/RedisForeachScenario.cs new file mode 100644 index 000000000..5fb2c3580 --- /dev/null +++ b/test/WorkflowCore.Tests.Redis/Scenarios/RedisForeachScenario.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.Redis.Scenarios +{ + [Collection("Redis collection")] + public class RedisForeachScenario : ForeachScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseRedisPersistence(RedisDockerSetup.ConnectionString, "scenario-")); + } + } +} diff --git a/test/WorkflowCore.Tests.Redis/Scenarios/RedisIfScenario.cs b/test/WorkflowCore.Tests.Redis/Scenarios/RedisIfScenario.cs new file mode 100644 index 000000000..744e2307f --- /dev/null +++ b/test/WorkflowCore.Tests.Redis/Scenarios/RedisIfScenario.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.Redis.Scenarios +{ + [Collection("Redis collection")] + public class RedisIfScenario : IfScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseRedisPersistence(RedisDockerSetup.ConnectionString, "scenario-")); + } + } +} diff --git a/test/WorkflowCore.Tests.Redis/Scenarios/RedisWhileScenario.cs b/test/WorkflowCore.Tests.Redis/Scenarios/RedisWhileScenario.cs new file mode 100644 index 000000000..06d422ac8 --- /dev/null +++ b/test/WorkflowCore.Tests.Redis/Scenarios/RedisWhileScenario.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.Redis.Scenarios +{ + [Collection("Redis collection")] + public class RedisWhileScenario : WhileScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseRedisPersistence(RedisDockerSetup.ConnectionString, "scenario-")); + } + } +} diff --git a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj new file mode 100644 index 000000000..6fa33c51a --- /dev/null +++ b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp2.1 + + false + + + + + + + + + + + + + + + + + + diff --git a/test/WorkflowCore.UnitTests/MemberMapParameterTests.cs b/test/WorkflowCore.UnitTests/Models/MemberMapParameterTests.cs similarity index 100% rename from test/WorkflowCore.UnitTests/MemberMapParameterTests.cs rename to test/WorkflowCore.UnitTests/Models/MemberMapParameterTests.cs From b6b14c5fcf46f04b1fda9fe631c7a20507026533 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 27 Jan 2019 14:19:02 -0800 Subject: [PATCH 100/462] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9de9b5c0e..b6dbe475c 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ There are several persistence providers available as separate Nuget packages. * [PostgreSQL](src/providers/WorkflowCore.Persistence.PostgreSQL) * [Sqlite](src/providers/WorkflowCore.Persistence.Sqlite) * [MySQL](src/providers/WorkflowCore.Persistence.MySQL) -* Redis *(coming soon...)* +* [Redis](src/providers/WorkflowCore.Providers.Redis) ## Search @@ -173,7 +173,7 @@ These are also available as separate Nuget packages. * **Daniel Gerlag** - *Initial work* * **Jackie Ja** -* **Aaron Scribnor** +* **Aaron Scribner** * **Roberto Paterlini** ## Ports From a50bb6abdd8d8aeed8f5e537d9fe3f75750ab7ba Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 27 Jan 2019 14:19:52 -0800 Subject: [PATCH 101/462] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b6dbe475c..07c05789b 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ These are also available as separate Nuget packages. * [JWorkflow (Java)](https://github.com/danielgerlag/jworkflow) * [workflow-es (Node.js)](https://github.com/danielgerlag/workflow-es) +* [liteflow (Python)](https://github.com/danielgerlag/liteflow) * [workflow_rb (Ruby)](https://github.com/danielgerlag/workflow_rb) ## License From a1e7f3a374542d9487a2197353fd54c56c2c3baa Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 27 Jan 2019 14:20:33 -0800 Subject: [PATCH 102/462] Update README.md --- src/providers/WorkflowCore.Providers.Redis/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Providers.Redis/README.md b/src/providers/WorkflowCore.Providers.Redis/README.md index bad88ac9d..9edc8f31e 100644 --- a/src/providers/WorkflowCore.Providers.Redis/README.md +++ b/src/providers/WorkflowCore.Providers.Redis/README.md @@ -32,7 +32,7 @@ Use the `IServiceCollection` extension methods when building your service provid ```C# services.AddWorkflow(cfg => { - cfg.UseRedisPersistence("localhost:6379", "app-name"); + cfg.UseRedisPersistence("localhost:6379", "app-name"); cfg.UseRedisLocking("localhost:6379"); cfg.UseRedisQueues("localhost:6379", "app-name"); cfg.UseRedisEventHub("localhost:6379", "channel-name") From e432405a15d175b0f9b46c99ef40260bab03480e Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 31 Jan 2019 08:35:36 -0800 Subject: [PATCH 103/462] readme --- src/samples/WebApiSample/README.md | 76 +++++++++++++++++++ src/samples/WebApiSample/WebApiSample.sln | 7 +- .../Controllers/WorkflowsController.cs | 3 +- 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 src/samples/WebApiSample/README.md diff --git a/src/samples/WebApiSample/README.md b/src/samples/WebApiSample/README.md new file mode 100644 index 000000000..cbe406b03 --- /dev/null +++ b/src/samples/WebApiSample/README.md @@ -0,0 +1,76 @@ + +# Using with ASP.NET Core + +This sample will use `docker-compose` to fire up instances of MongoDB and Elasticsearch to which the sample application will connect. + +## How to configure within an ASP.NET Core application + +In your startup class, use the `AddWorkflow` extension method to configure workflow core services, and then register your workflows and start the host when you configure the app. +```c# +public class Startup +{ + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddMvc(); + services.AddWorkflow(cfg => + { + cfg.UseMongoDB(@"mongodb://mongo:27017", "workflow"); + cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://elastic:9200")), "workflows"); + }); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseMvc(); + + var host = app.ApplicationServices.GetService(); + host.RegisterWorkflow(); + host.Start(); + } +} +``` + +## Usage + +Now simply inject the services you require into your controllers +* IWorkflowController +* IWorkflowHost +* ISearchIndex +* IPersistenceProvider + +```c# +public class WorkflowsController : Controller +{ + private readonly IWorkflowController _workflowService; + private readonly IWorkflowRegistry _registry; + private readonly IPersistenceProvider _workflowStore; + private readonly ISearchIndex _searchService; + + public WorkflowsController(IWorkflowController workflowService, ISearchIndex searchService, IWorkflowRegistry registry, IPersistenceProvider workflowStore) + { + _workflowService = workflowService; + _workflowStore = workflowStore; + _registry = registry; + _searchService = searchService; + } + + public Task Suspend(string id) + { + return _workflowService.SuspendWorkflow(id); + } + + ... +} +``` \ No newline at end of file diff --git a/src/samples/WebApiSample/WebApiSample.sln b/src/samples/WebApiSample/WebApiSample.sln index c37bd7b35..f2e46941d 100644 --- a/src/samples/WebApiSample/WebApiSample.sln +++ b/src/samples/WebApiSample/WebApiSample.sln @@ -3,10 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.28307.168 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiSample", "WebApiSample\WebApiSample.csproj", "{14489389-A65D-4993-8DE2-F51701A5AF60}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiSample", "WebApiSample\WebApiSample.csproj", "{14489389-A65D-4993-8DE2-F51701A5AF60}" EndProject Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{2D5D708D-7EA1-48A9-ABF0-64CCC6026435}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6545FD2F-EBA5-4C65-8257-2C1E34AD2FAA}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/src/samples/WebApiSample/WebApiSample/Controllers/WorkflowsController.cs b/src/samples/WebApiSample/WebApiSample/Controllers/WorkflowsController.cs index 694d9b05b..b61d50d76 100644 --- a/src/samples/WebApiSample/WebApiSample/Controllers/WorkflowsController.cs +++ b/src/samples/WebApiSample/WebApiSample/Controllers/WorkflowsController.cs @@ -28,8 +28,7 @@ public WorkflowsController(IWorkflowController workflowService, ISearchIndex sea _registry = registry; _searchService = searchService; } - - + [HttpGet] public async Task Get(string terms, WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take = 10) { From 8bcd4939275e6512931d9679c810cfae85fb5272 Mon Sep 17 00:00:00 2001 From: Mario Andron Date: Fri, 1 Feb 2019 06:55:57 +0200 Subject: [PATCH 104/462] Added intetgration scenarios to test IoC scope for MS IoC container and the AutoFac IoC container --- .../Scenarios/DiScenario.cs | 255 ++++++++++++++++++ .../WorkflowCore.IntegrationTests.csproj | 1 + test/WorkflowCore.Testing/WorkflowTest.cs | 2 +- 3 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs new file mode 100644 index 000000000..3c8a093c8 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs @@ -0,0 +1,255 @@ +using System; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using WorkflowCore.Testing; +using Microsoft.Extensions.DependencyInjection; +using Autofac.Extensions.DependencyInjection; +using Autofac; +using System.Diagnostics; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class DiWorkflow : IWorkflow + { + public string Id => "DiWorkflow"; + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Output(_ => _.instance1, _ => _.dependency1.Instance) + .Output(_ => _.instance2, _ => _.dependency2.dependency1.Instance) + .Then(context => + { + return ExecutionResult.Next(); + }); + } + } + + public class DiData + { + public int instance1 { get; set; } = -1; + public int instance2 { get; set; } = -1; + } + + public class Dependency1 + { + public static int InstanceCounter = 0; + + public int Instance { get; set; } = ++InstanceCounter; + } + + public class Dependency2 + { + public readonly Dependency1 dependency1; + + public Dependency2(Dependency1 dependency1) + { + this.dependency1 = dependency1; + } + } + + public class DiStep1 : StepBody + { + public readonly Dependency1 dependency1; + public readonly Dependency2 dependency2; + + public DiStep1(Dependency1 dependency1, Dependency2 dependency2) + { + this.dependency1 = dependency1; + this.dependency2 = dependency2; + } + + public override ExecutionResult Run(IStepExecutionContext context) + { + return ExecutionResult.Next(); + } + } + + /// + /// The DI scenarios are design to test whether the scoped / transient dependecies are honoured with + /// various IoC container implementations. The basic premise is that a step has a dependency on + /// two services, one of which has a dependency on the other. + /// + /// We then use the instance numbers of the services to determine whether the container has created a + /// transient instance or a scoped instance + /// + /// if step.dependency2.dependency1.instance == step.dependency1.instance then + /// we can be assured that dependency1 was created in the same scope as dependency 2 + /// + /// otherwise if the instances are different, they were created as transient + /// + /// + public abstract class DiScenario : WorkflowTest + { + protected void ConfigureHost(IServiceProvider serviceProvider) + { + PersistenceProvider = serviceProvider.GetService(); + Host = serviceProvider.GetService(); + Host.RegisterWorkflow(); + Host.OnStepError += Host_OnStepError; + Host.Start(); + } + } + + /// + /// Because of the static InMemory Persistence provider, this test must run in issolation + /// to prevent other hosts from picking up steps intended for this host and incorrectly + /// cross-referencing the scoped / transient IoC container for step constrcution + /// + [CollectionDefinition("DiMsTransientScenario", DisableParallelization = true)] + [Collection("DiMsTransientScenario")] + public class DiMsTransientScenario : DiScenario + { + public DiMsTransientScenario() + { + //setup dependency injection + IServiceCollection services = new ServiceCollection(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddLogging(); + ConfigureServices(services); + + var serviceProvider = services.BuildServiceProvider(); + ConfigureHost(serviceProvider); + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(new DiData()); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(5)); + var data = GetData(workflowId); + + // DI provider should have created two transient instances, with different instance ids + data.instance1.Should().NotBe(-1); + data.instance2.Should().NotBe(-1); + data.instance1.Should().NotBe(data.instance2); + } + } + + /// + /// Because of the static InMemory Persistence provider, this test must run in issolation + /// to prevent other hosts from picking up steps intended for this host and incorrectly + /// cross-referencing the scoped / transient IoC container for step constrcution + /// + [CollectionDefinition("DiMsScopedScenario", DisableParallelization = true)] + [Collection("DiMsScopedScenario")] + public class DiMsScopedScenario : DiScenario + { + public DiMsScopedScenario() + { + //setup dependency injection + IServiceCollection services = new ServiceCollection(); + services.AddScoped(); + services.AddScoped(); + services.AddTransient(); + services.AddLogging(); + ConfigureServices(services); + + var serviceProvider = services.BuildServiceProvider(); + ConfigureHost(serviceProvider); + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(new DiData()); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(5)); + var data = GetData(workflowId); + + // scope provider should have created one scoped instance, with the same instance ids + data.instance1.Should().NotBe(-1); + data.instance2.Should().NotBe(-1); + data.instance1.Should().Be(data.instance2); + } + } + + /// + /// Because of the static InMemory Persistence provider, this test must run in issolation + /// to prevent other hosts from picking up steps intended for this host and incorrectly + /// cross-referencing the scoped / transient IoC container for step constrcution + /// + [CollectionDefinition("DiAutoFacTransientScenario", DisableParallelization = true)] + [Collection("DiAutoFacTransientScenario")] + public class DiAutoFacTransientScenario : DiScenario + { + public DiAutoFacTransientScenario() + { + //setup dependency injection + IServiceCollection services = new ServiceCollection(); + services.AddLogging(); + ConfigureServices(services); + + //setup dependency injection + var builder = new ContainerBuilder(); + builder.Populate(services); + builder.RegisterType().InstancePerDependency(); + builder.RegisterType().InstancePerDependency(); + builder.RegisterType().InstancePerDependency(); + var container = builder.Build(); + + var serviceProvider = new AutofacServiceProvider(container); + ConfigureHost(serviceProvider); + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(new DiData()); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(5)); + var data = GetData(workflowId); + + // scope provider should have created one scoped instance, with the same instance ids + data.instance1.Should().NotBe(-1); + data.instance2.Should().NotBe(-1); + data.instance1.Should().NotBe(data.instance2); + } + } + + /// + /// Because of the static InMemory Persistence provider, this test must run in issolation + /// to prevent other hosts from picking up steps intended for this host and incorrectly + /// cross-referencing the scoped / transient IoC container for step constrcution + /// + [CollectionDefinition("DiAutoFacScopedScenario", DisableParallelization = true)] + [Collection("DiAutoFacScopedScenario")] + public class DiAutoFacScopedScenario : DiScenario + { + public DiAutoFacScopedScenario() + { + //setup dependency injection + IServiceCollection services = new ServiceCollection(); + services.AddLogging(); + ConfigureServices(services); + + //setup dependency injection + var builder = new ContainerBuilder(); + builder.Populate(services); + builder.RegisterType().InstancePerLifetimeScope(); + builder.RegisterType().InstancePerLifetimeScope(); + builder.RegisterType().InstancePerLifetimeScope(); + var container = builder.Build(); + + var serviceProvider = new AutofacServiceProvider(container); + ConfigureHost(serviceProvider); + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(null); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(5)); + var data = GetData(workflowId); + + // scope provider should have created one scoped instance, with the same instance ids + data.instance1.Should().NotBe(-1); + data.instance2.Should().NotBe(-1); + data.instance1.Should().Be(data.instance2); + } + } +} diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index ddde2f87a..a2a88fb71 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -18,6 +18,7 @@ + diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/test/WorkflowCore.Testing/WorkflowTest.cs index 3ab2ec91b..9c7d0dacc 100644 --- a/test/WorkflowCore.Testing/WorkflowTest.cs +++ b/test/WorkflowCore.Testing/WorkflowTest.cs @@ -39,7 +39,7 @@ protected virtual void Setup() Host.Start(); } - private void Host_OnStepError(WorkflowInstance workflow, WorkflowStep step, Exception exception) + protected void Host_OnStepError(WorkflowInstance workflow, WorkflowStep step, Exception exception) { UnhandledStepErrors.Add(new StepError() { From 8df0ceeee261b7959c08e71dbb9cd90579beaf4b Mon Sep 17 00:00:00 2001 From: Mario Andron Date: Fri, 1 Feb 2019 10:34:41 +0200 Subject: [PATCH 105/462] appease the codacy gods --- .../WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs index 3c8a093c8..81a782264 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs @@ -37,14 +37,14 @@ public class DiData public class Dependency1 { - public static int InstanceCounter = 0; + private static int InstanceCounter = 0; public int Instance { get; set; } = ++InstanceCounter; } public class Dependency2 { - public readonly Dependency1 dependency1; + public Dependency1 dependency1 { get; private set; } public Dependency2(Dependency1 dependency1) { @@ -54,8 +54,8 @@ public Dependency2(Dependency1 dependency1) public class DiStep1 : StepBody { - public readonly Dependency1 dependency1; - public readonly Dependency2 dependency2; + public Dependency1 dependency1 { get; private set; } + public Dependency2 dependency2 { get; private set; } public DiStep1(Dependency1 dependency1, Dependency2 dependency2) { From 0bc16311f3e0cf3b60d74fc9b60ab53eb36647fd Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 3 Feb 2019 07:51:23 -0800 Subject: [PATCH 106/462] bump version --- ReleaseNotes/1.8.1.md | 6 ++++++ WorkflowCore.sln | 3 ++- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- .../Services/DynamoLockProvider.cs | 16 +++++++--------- 4 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 ReleaseNotes/1.8.1.md diff --git a/ReleaseNotes/1.8.1.md b/ReleaseNotes/1.8.1.md new file mode 100644 index 000000000..119083765 --- /dev/null +++ b/ReleaseNotes/1.8.1.md @@ -0,0 +1,6 @@ +# Workflow Core 1.8.1 + +Thank you to @MarioAndron + +This release adds a feature where a DI scope is created around the construction of steps that are registered with your IoC container. +This enables steps to consume services registered as `scoped`. \ No newline at end of file diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 1393240ae..181566fd6 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -96,6 +96,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.6.9.md = ReleaseNotes\1.6.9.md ReleaseNotes\1.7.0.md = ReleaseNotes\1.7.0.md ReleaseNotes\1.8.0.md = ReleaseNotes\1.8.0.md + ReleaseNotes\1.8.1.md = ReleaseNotes\1.8.1.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" @@ -130,7 +131,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Providers.Elas EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.Elasticsearch", "test\WorkflowCore.Tests.Elasticsearch\WorkflowCore.Tests.Elasticsearch.csproj", "{44644716-0CE8-4837-B189-AB65AE2106AA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.Redis", "test\WorkflowCore.Tests.Redis\WorkflowCore.Tests.Redis.csproj", "{78217204-B873-40B9-8875-E3925B2FBCEC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.Redis", "test\WorkflowCore.Tests.Redis\WorkflowCore.Tests.Redis.csproj", "{78217204-B873-40B9-8875-E3925B2FBCEC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 837d40ff6..aaef3c388 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.8.0 - 1.8.0.0 - 1.8.0.0 + 1.8.1 + 1.8.1.0 + 1.8.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs index ce1458b60..2a7c2f7bd 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -77,16 +77,14 @@ public async Task AcquireLock(string Id, CancellationToken cancellationTok public async Task ReleaseLock(string Id) { - if (_mutex.WaitOne()) + _mutex.WaitOne(); + try { - try - { - _localLocks.Remove(Id); - } - finally - { - _mutex.Set(); - } + _localLocks.Remove(Id); + } + finally + { + _mutex.Set(); } try From 791287d3e09abffdba69a5300c02241cf760fe91 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 17 Feb 2019 07:13:00 -0800 Subject: [PATCH 107/462] Compensation behavior in nested saga (#263) --- .../ErrorHandlers/CompensateHandler.cs | 29 ++-- .../Services/ExecutionPointerFactory.cs | 5 +- src/WorkflowCore/WorkflowCore.csproj | 6 +- .../WorkflowCore.Users/Primitives/UserTask.cs | 4 +- .../WorkflowCore.Users.csproj | 6 +- .../Scenarios/NestedRetrySagaScenario.cs | 90 +++++++++++ .../RetrySagaWithUserTaskScenario.cs | 153 ++++++++++++++++++ 7 files changed, 270 insertions(+), 23 deletions(-) create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/NestedRetrySagaScenario.cs create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaWithUserTaskScenario.cs diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index de3e98c01..a6708ad08 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -27,13 +27,9 @@ public CompensateHandler(IExecutionPointerFactory pointerFactory, ILifeCycleEven public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer exceptionPointer, WorkflowStep exceptionStep, Exception exception, Queue bubbleUpQueue) { - var scope = new Stack(exceptionPointer.Scope); + var scope = new Stack(exceptionPointer.Scope.Reverse()); scope.Push(exceptionPointer.Id); - - exceptionPointer.Active = false; - exceptionPointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); - exceptionPointer.Status = PointerStatus.Failed; - + while (scope.Any()) { var pointerId = scope.Pop(); @@ -42,14 +38,19 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP var resume = true; var revert = false; - - if (scope.Any()) + + var txnStack = new Stack(scope.Reverse()); + while (txnStack.Count > 0) { - var parentId = scope.Peek(); + var parentId = txnStack.Pop(); var parentPointer = workflow.ExecutionPointers.FindById(parentId); var parentStep = def.Steps.First(x => x.Id == parentPointer.StepId); - resume = parentStep.ResumeChildrenAfterCompensation; - revert = parentStep.RevertChildrenAfterCompensation; + if ((!parentStep.ResumeChildrenAfterCompensation) || (parentStep.RevertChildrenAfterCompensation)) + { + resume = parentStep.ResumeChildrenAfterCompensation; + revert = parentStep.RevertChildrenAfterCompensation; + break; + } } if ((scopeStep.ErrorBehavior ?? WorkflowErrorHandling.Compensate) != WorkflowErrorHandling.Compensate) @@ -58,10 +59,12 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP continue; } + scopePointer.Active = false; + scopePointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); + scopePointer.Status = PointerStatus.Failed; + if (scopeStep.CompensationStepId.HasValue) { - scopePointer.Active = false; - scopePointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); scopePointer.Status = PointerStatus.Compensated; var compensationPointer = _pointerFactory.BuildCompensationPointer(def, scopePointer, exceptionPointer, scopeStep.CompensationStepId.Value); diff --git a/src/WorkflowCore/Services/ExecutionPointerFactory.cs b/src/WorkflowCore/Services/ExecutionPointerFactory.cs index 927e06e4e..43ff0b5cc 100644 --- a/src/WorkflowCore/Services/ExecutionPointerFactory.cs +++ b/src/WorkflowCore/Services/ExecutionPointerFactory.cs @@ -9,7 +9,6 @@ namespace WorkflowCore.Services { public class ExecutionPointerFactory : IExecutionPointerFactory { - public ExecutionPointer BuildGenesisPointer(WorkflowDefinition def) { return new ExecutionPointer @@ -41,8 +40,8 @@ public ExecutionPointer BuildNextPointer(WorkflowDefinition def, ExecutionPointe public ExecutionPointer BuildChildPointer(WorkflowDefinition def, ExecutionPointer pointer, int childDefinitionId, object branch) { var childPointerId = GenerateId(); - var childScope = new Stack(pointer.Scope); - childScope.Push(pointer.Id); + var childScope = new List(pointer.Scope); + childScope.Insert(0, pointer.Id); pointer.Children.Add(childPointerId); return new ExecutionPointer() diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index aaef3c388..55b2e2337 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.8.1 - 1.8.1.0 - 1.8.1.0 + 1.8.2 + 1.8.2.0 + 1.8.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs b/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs index 536e56d84..5f67ca443 100644 --- a/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs +++ b/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs @@ -80,7 +80,9 @@ private void SetupEscalations(IStepExecutionContext context) Id = Guid.NewGuid().ToString(), PredecessorId = context.ExecutionPointer.Id, StepId = esc.Id, - StepName = esc.Name + StepName = esc.Name, + Status = PointerStatus.Pending, + Scope = new List(context.ExecutionPointer.Scope) }); } } diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 8c7bd192d..9bb4c502f 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -18,9 +18,9 @@ false false Provides extensions for Workflow Core to enable human workflows. - 1.8.0 - 1.8.0.0 - 1.8.0.0 + 1.8.2 + 1.8.2.0 + 1.8.2.0 diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/NestedRetrySagaScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/NestedRetrySagaScenario.cs new file mode 100644 index 000000000..dd4d2d720 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/NestedRetrySagaScenario.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using System.Linq; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class NestedRetrySagaScenario : WorkflowTest + { + public class MyDataClass + { + } + + public class Workflow : IWorkflow + { + public static int Event1Fired; + public static int Event2Fired; + public static int Event3Fired; + public static int TailEventFired; + public static int Compensation1Fired; + public static int Compensation2Fired; + public static int Compensation3Fired; + public static int Compensation4Fired; + + public string Id => "NestedRetrySagaWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .CompensateWith(context => Compensation1Fired++) + .Saga(x => x + .StartWith(context => ExecutionResult.Next()) + .CompensateWith(context => Compensation2Fired++) + .If(data => true) + .Do(i => i + .StartWith(context => + { + Event1Fired++; + if (Event1Fired < 3) + throw new Exception(); + Event2Fired++; + }) + .CompensateWith(context => Compensation3Fired++) + ) + .Then(context => Event3Fired++) + .CompensateWith(context => Compensation4Fired++) + ) + .OnError(WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(1)) + .Then(context => TailEventFired++); + } + } + + public NestedRetrySagaScenario() + { + Setup(); + Workflow.Event1Fired = 0; + Workflow.Event2Fired = 0; + Workflow.Event3Fired = 0; + Workflow.Compensation1Fired = 0; + Workflow.Compensation2Fired = 0; + Workflow.Compensation3Fired = 0; + Workflow.Compensation4Fired = 0; + Workflow.TailEventFired = 0; + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(new MyDataClass()); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(2); + Workflow.Event1Fired.Should().Be(3); + Workflow.Event2Fired.Should().Be(1); + Workflow.Event3Fired.Should().Be(1); + Workflow.Compensation1Fired.Should().Be(0); + Workflow.Compensation2Fired.Should().Be(2); + Workflow.Compensation3Fired.Should().Be(2); + Workflow.Compensation4Fired.Should().Be(0); + Workflow.TailEventFired.Should().Be(1); + } + } +} diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaWithUserTaskScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaWithUserTaskScenario.cs new file mode 100644 index 000000000..cf397fd8c --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaWithUserTaskScenario.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using System.Linq; +using System.Threading.Tasks; +using WorkflowCore.Testing; +using WorkflowCore.Users.Models; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class RetrySagaWithUserTaskScenario : WorkflowTest + { + public class MyDataClass + { + } + + public class Workflow : IWorkflow + { + public static int Event1Fired; + public static int Event2Fired; + public static int Event3Fired; + public static int TailEventFired; + public static int Compensation1Fired; + public static int Compensation2Fired; + public static int Compensation3Fired; + public static int Compensation4Fired; + + public string Id => "RetrySagaWithUserTaskWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .CompensateWith(context => Compensation1Fired++) + .Saga(x => x + .StartWith(context => ExecutionResult.Next()) + .CompensateWith(context => Compensation2Fired++) + .UserTask("prompt", data => "assigner") + .WithOption("a", "Option A") + .Do(wb => wb + .StartWith(context => ExecutionResult.Next()) + .Then(context => + { + Event1Fired++; + if (Event1Fired < 3) + throw new Exception(); + Event2Fired++; + }) + .CompensateWith(context => Compensation3Fired++) + .Then(context => Event3Fired++) + .CompensateWith(context => Compensation4Fired++) + ) + ) + .OnError(WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(1)) + .Then(context => TailEventFired++); + } + } + + public RetrySagaWithUserTaskScenario() + { + Setup(); + Workflow.Event1Fired = 0; + Workflow.Event2Fired = 0; + Workflow.Event3Fired = 0; + Workflow.Compensation1Fired = 0; + Workflow.Compensation2Fired = 0; + Workflow.Compensation3Fired = 0; + Workflow.Compensation4Fired = 0; + Workflow.TailEventFired = 0; + } + + [Fact] + public async Task Scenario() + { + var workflowId = StartWorkflow(new MyDataClass()); + var instance = await Host.PersistenceStore.GetWorkflowInstance(workflowId); + + string oldUserOptionKey = null; + for (var i = 0; i != 3; ++i) + { + var userOptions = await WaitForDifferentUserStepAsync(instance, TimeSpan.FromSeconds(1), oldUserOptionKey); + userOptions.Count.Should().Be(1); + + var userOption = userOptions.Single(); + userOption.Prompt.Should().Be("prompt"); + userOption.AssignedPrincipal.Should().Be("assigner"); + userOption.Options.Count.Should().Be(1); + + var selectionOption = userOption.Options.Single(); + selectionOption.Key.Should().Be("Option A"); + selectionOption.Value.Should().Be("a"); + await Host.PublishUserAction(userOption.Key, string.Empty, selectionOption.Value); + + oldUserOptionKey = userOption.Key; + } + + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(2); + Workflow.Event1Fired.Should().Be(3); + Workflow.Event2Fired.Should().Be(1); + Workflow.Event3Fired.Should().Be(1); + Workflow.Compensation1Fired.Should().Be(0); + Workflow.Compensation2Fired.Should().Be(2); + Workflow.Compensation3Fired.Should().Be(2); + Workflow.Compensation4Fired.Should().Be(0); + Workflow.TailEventFired.Should().Be(1); + } + + private static async Task> WaitForDifferentUserStepAsync( + WorkflowInstance instance, + TimeSpan timeout, + string oldUserActionKey = null) + { + var startTime = DateTime.UtcNow; + + while (DateTime.UtcNow - startTime <= timeout) + { + var userActions = await WaitForUserStepAsync(instance); + + if (oldUserActionKey != null && userActions.Any(x => x.Key == oldUserActionKey)) + { + continue; + } + + return userActions; + } + + return Array.Empty(); + } + + private static async Task> WaitForUserStepAsync(WorkflowInstance instance) + { + var delayCount = 200; + var openActions = instance.GetOpenUserActions()?.ToList(); + while ((openActions?.Count ?? 0) == 0) + { + await Task.Delay(TimeSpan.FromMilliseconds(10)); + openActions = instance.GetOpenUserActions()?.ToList(); + if (delayCount-- == 0) + { + break; + } + } + + return openActions; + } + } +} \ No newline at end of file From b4cba8d5615a98325397c1e2f9980f501e46630b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 24 Feb 2019 07:44:50 -0800 Subject: [PATCH 108/462] Misc improvements (#269) --- src/WorkflowCore/Interface/IQueueProvider.cs | 2 +- src/WorkflowCore/Interface/IStepBuilder.cs | 14 ++++ src/WorkflowCore/Models/StepOutcome.cs | 2 +- src/WorkflowCore/Models/WorkflowDefinition.cs | 2 +- src/WorkflowCore/Models/WorkflowStep.cs | 2 +- .../Models/WorkflowStepCollection.cs | 81 +++++++++++++++++++ .../SingleNodeQueueProvider.cs | 33 ++++---- .../DefinitionStorage/DefinitionLoader.cs | 26 +++--- .../ErrorHandlers/CompensateHandler.cs | 6 +- .../Services/ExecutionPointerFactory.cs | 8 +- .../Services/ExecutionResultProcessor.cs | 4 +- .../Services/FluentBuilders/StepBuilder.cs | 24 +++++- .../FluentBuilders/WorkflowBuilder.cs | 32 ++++++-- src/WorkflowCore/Services/WorkflowExecutor.cs | 4 +- src/WorkflowCore/WorkflowCore.csproj | 6 +- .../Services/SQSQueueProvider.cs | 32 ++------ .../WorkflowCore.Providers.AWS.csproj | 4 +- .../Services/AzureStorageQueueProvider.cs | 37 +++------ .../WorkflowCore.Providers.Azure.csproj | 6 +- .../Services/RedisQueueProvider.cs | 26 +++--- .../WorkflowCore.Providers.Redis.csproj | 2 +- .../Services/RabbitMQProvider.cs | 2 + ...orkflowCore.QueueProviders.RabbitMQ.csproj | 6 +- .../Services/QueueConfigProvider.cs | 29 +++---- .../SqlServerQueueProviderMigrator.cs | 3 +- ...rkflowCore.QueueProviders.SqlServer.csproj | 2 +- .../Scenarios/AttachScenario.cs | 64 +++++++++++++++ .../DefinitionLoaderTests.cs | 4 +- .../Services/WorkflowExecutorFixture.cs | 2 +- 29 files changed, 303 insertions(+), 162 deletions(-) create mode 100644 src/WorkflowCore/Models/WorkflowStepCollection.cs create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/AttachScenario.cs diff --git a/src/WorkflowCore/Interface/IQueueProvider.cs b/src/WorkflowCore/Interface/IQueueProvider.cs index dfbc2f14e..7c73607e0 100644 --- a/src/WorkflowCore/Interface/IQueueProvider.cs +++ b/src/WorkflowCore/Interface/IQueueProvider.cs @@ -32,5 +32,5 @@ public interface IQueueProvider : IDisposable Task Stop(); } - public enum QueueType { Workflow = 0, Event = 1 } + public enum QueueType { Workflow = 0, Event = 1, Index = 2 } } diff --git a/src/WorkflowCore/Interface/IStepBuilder.cs b/src/WorkflowCore/Interface/IStepBuilder.cs index df6f92404..174956a1d 100644 --- a/src/WorkflowCore/Interface/IStepBuilder.cs +++ b/src/WorkflowCore/Interface/IStepBuilder.cs @@ -21,6 +21,13 @@ public interface IStepBuilder /// IStepBuilder Name(string name); + /// + /// Specifies a custom Id to reference this step + /// + /// A custom Id to reference this step + /// + IStepBuilder Id(string id); + /// /// Specify the next step in the workflow /// @@ -51,6 +58,13 @@ public interface IStepBuilder /// IStepBuilder Then(Action body); + /// + /// Specify the next step in the workflow by Id + /// + /// + /// + IStepBuilder Attach(string id); + /// /// Configure an outcome for this step, then wire it to another step /// diff --git a/src/WorkflowCore/Models/StepOutcome.cs b/src/WorkflowCore/Models/StepOutcome.cs index 3c5b32dcb..cf4e1a0b6 100644 --- a/src/WorkflowCore/Models/StepOutcome.cs +++ b/src/WorkflowCore/Models/StepOutcome.cs @@ -16,7 +16,7 @@ public Expression> Value public string Label { get; set; } - public string Tag { get; set; } + public string ExternalNextStepId { get; set; } public object GetValue(object data) { diff --git a/src/WorkflowCore/Models/WorkflowDefinition.cs b/src/WorkflowCore/Models/WorkflowDefinition.cs index df0695087..f39a563ed 100644 --- a/src/WorkflowCore/Models/WorkflowDefinition.cs +++ b/src/WorkflowCore/Models/WorkflowDefinition.cs @@ -12,7 +12,7 @@ public class WorkflowDefinition public string Description { get; set; } - public List Steps { get; set; } + public WorkflowStepCollection Steps { get; set; } = new WorkflowStepCollection(); public Type DataType { get; set; } diff --git a/src/WorkflowCore/Models/WorkflowStep.cs b/src/WorkflowCore/Models/WorkflowStep.cs index 00bcf26c4..73fc37b77 100644 --- a/src/WorkflowCore/Models/WorkflowStep.cs +++ b/src/WorkflowCore/Models/WorkflowStep.cs @@ -14,7 +14,7 @@ public abstract class WorkflowStep public virtual string Name { get; set; } - public virtual string Tag { get; set; } + public virtual string ExternalId { get; set; } public virtual List Children { get; set; } = new List(); diff --git a/src/WorkflowCore/Models/WorkflowStepCollection.cs b/src/WorkflowCore/Models/WorkflowStepCollection.cs new file mode 100644 index 000000000..90c2e4710 --- /dev/null +++ b/src/WorkflowCore/Models/WorkflowStepCollection.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WorkflowCore.Models +{ + public class WorkflowStepCollection : ICollection + { + private readonly Dictionary _dictionary = new Dictionary(); + + public WorkflowStepCollection() + { + } + + public WorkflowStepCollection(int capacity) + { + _dictionary = new Dictionary(capacity); + } + + public WorkflowStepCollection(ICollection steps) + { + foreach (var step in steps) + { + Add(step); + } + } + + public IEnumerator GetEnumerator() + { + return _dictionary.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public WorkflowStep FindById(int id) + { + if (!_dictionary.ContainsKey(id)) + return null; + + return _dictionary[id]; + } + + public void Add(WorkflowStep item) + { + _dictionary.Add(item.Id, item); + } + + public void Clear() + { + _dictionary.Clear(); + } + + public bool Contains(WorkflowStep item) + { + return _dictionary.ContainsValue(item); + } + + public void CopyTo(WorkflowStep[] array, int arrayIndex) + { + _dictionary.Values.CopyTo(array, arrayIndex); + } + + public bool Remove(WorkflowStep item) + { + return _dictionary.Remove(item.Id); + } + + public WorkflowStep Find(Predicate match) + { + return _dictionary.Values.FirstOrDefault(x => match(x)); + } + + public int Count => _dictionary.Count; + public bool IsReadOnly => false; + } +} diff --git a/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs b/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs index 2f3b5bb45..38a8c5c4f 100644 --- a/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using WorkflowCore.Interface; @@ -12,48 +13,42 @@ namespace WorkflowCore.Services /// public class SingleNodeQueueProvider : IQueueProvider { - - private readonly BlockingCollection _runQueue = new BlockingCollection(); - private readonly BlockingCollection _eventQueue = new BlockingCollection(); + + private readonly Dictionary> _queues = new Dictionary>() + { + [QueueType.Workflow] = new BlockingCollection(), + [QueueType.Event] = new BlockingCollection(), + [QueueType.Index] = new BlockingCollection() + }; public bool IsDequeueBlocking => true; public async Task QueueWork(string id, QueueType queue) { - SelectQueue(queue).Add(id); + _queues[queue].Add(id); } public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) { - if (SelectQueue(queue).TryTake(out string id, 100, cancellationToken)) + if (_queues[queue].TryTake(out string id, 100, cancellationToken)) return id; return null; } - public async Task Start() + public Task Start() { + return Task.CompletedTask; } - public async Task Stop() + public Task Stop() { + return Task.CompletedTask; } public void Dispose() { } - - private BlockingCollection SelectQueue(QueueType queue) - { - switch (queue) - { - case QueueType.Workflow: - return _runQueue; - case QueueType.Event: - return _eventQueue; - } - return null; - } } diff --git a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs index 6959990f6..fd44f7873 100644 --- a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs +++ b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs @@ -53,9 +53,9 @@ private WorkflowDefinition Convert(DefinitionSourceV1 source) } - private List ConvertSteps(ICollection source, Type dataType) + private WorkflowStepCollection ConvertSteps(ICollection source, Type dataType) { - var result = new List(); + var result = new WorkflowStepCollection(); int i = 0; var stack = new Stack(source.Reverse()); var parents = new List(); @@ -87,7 +87,7 @@ private List ConvertSteps(ICollection source, Type d targetStep.Name = nextStep.Name; targetStep.ErrorBehavior = nextStep.ErrorBehavior; targetStep.RetryInterval = nextStep.RetryInterval; - targetStep.Tag = $"{nextStep.Id}"; + targetStep.ExternalId = $"{nextStep.Id}"; AttachInputs(nextStep, dataType, stepType, targetStep); AttachOutputs(nextStep, dataType, stepType, targetStep); @@ -114,7 +114,7 @@ private List ConvertSteps(ICollection source, Type d } if (!string.IsNullOrEmpty(nextStep.NextStepId)) - targetStep.Outcomes.Add(new StepOutcome() { Tag = $"{nextStep.NextStepId}" }); + targetStep.Outcomes.Add(new StepOutcome() { ExternalNextStepId = $"{nextStep.NextStepId}" }); result.Add(targetStep); @@ -123,26 +123,26 @@ private List ConvertSteps(ICollection source, Type d foreach (var step in result) { - if (result.Any(x => x.Tag == step.Tag && x.Id != step.Id)) - throw new WorkflowDefinitionLoadException($"Duplicate step Id {step.Tag}"); + if (result.Any(x => x.ExternalId == step.ExternalId && x.Id != step.Id)) + throw new WorkflowDefinitionLoadException($"Duplicate step Id {step.ExternalId}"); foreach (var outcome in step.Outcomes) { - if (result.All(x => x.Tag != outcome.Tag)) - throw new WorkflowDefinitionLoadException($"Cannot find step id {outcome.Tag}"); + if (result.All(x => x.ExternalId != outcome.ExternalNextStepId)) + throw new WorkflowDefinitionLoadException($"Cannot find step id {outcome.ExternalNextStepId}"); - outcome.NextStep = result.Single(x => x.Tag == outcome.Tag).Id; + outcome.NextStep = result.Single(x => x.ExternalId == outcome.ExternalNextStepId).Id; } } foreach (var parent in parents) { - var target = result.Single(x => x.Tag == parent.Id); + var target = result.Single(x => x.ExternalId == parent.Id); foreach (var branch in parent.Do) { var childTags = branch.Select(x => x.Id).ToList(); target.Children.AddRange(result - .Where(x => childTags.Contains(x.Tag)) + .Where(x => childTags.Contains(x.ExternalId)) .OrderBy(x => x.Id) .Select(x => x.Id) .Take(1) @@ -152,11 +152,11 @@ private List ConvertSteps(ICollection source, Type d foreach (var item in compensatables) { - var target = result.Single(x => x.Tag == item.Id); + var target = result.Single(x => x.ExternalId == item.Id); var tag = item.CompensateWith.Select(x => x.Id).FirstOrDefault(); if (tag != null) { - var compStep = result.FirstOrDefault(x => x.Tag == tag); + var compStep = result.FirstOrDefault(x => x.ExternalId == tag); if (compStep != null) target.CompensationStepId = compStep.Id; } diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index a6708ad08..aca56d6b6 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -34,7 +34,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP { var pointerId = scope.Pop(); var scopePointer = workflow.ExecutionPointers.FindById(pointerId); - var scopeStep = def.Steps.First(x => x.Id == scopePointer.StepId); + var scopeStep = def.Steps.FindById(scopePointer.StepId); var resume = true; var revert = false; @@ -44,7 +44,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP { var parentId = txnStack.Pop(); var parentPointer = workflow.ExecutionPointers.FindById(parentId); - var parentStep = def.Steps.First(x => x.Id == parentPointer.StepId); + var parentStep = def.Steps.FindById(parentPointer.StepId); if ((!parentStep.ResumeChildrenAfterCompensation) || (parentStep.RevertChildrenAfterCompensation)) { resume = parentStep.ResumeChildrenAfterCompensation; @@ -86,7 +86,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP foreach (var siblingPointer in prevSiblings) { - var siblingStep = def.Steps.First(x => x.Id == siblingPointer.StepId); + var siblingStep = def.Steps.FindById(siblingPointer.StepId); if (siblingStep.CompensationStepId.HasValue) { var compensationPointer = _pointerFactory.BuildCompensationPointer(def, siblingPointer, exceptionPointer, siblingStep.CompensationStepId.Value); diff --git a/src/WorkflowCore/Services/ExecutionPointerFactory.cs b/src/WorkflowCore/Services/ExecutionPointerFactory.cs index 43ff0b5cc..0ed6fe553 100644 --- a/src/WorkflowCore/Services/ExecutionPointerFactory.cs +++ b/src/WorkflowCore/Services/ExecutionPointerFactory.cs @@ -17,7 +17,7 @@ public ExecutionPointer BuildGenesisPointer(WorkflowDefinition def) StepId = 0, Active = true, Status = PointerStatus.Pending, - StepName = Enumerable.First(def.Steps, x => x.Id == 0).Name + StepName = def.Steps.FindById(0).Name }; } @@ -32,7 +32,7 @@ public ExecutionPointer BuildNextPointer(WorkflowDefinition def, ExecutionPointe Active = true, ContextItem = pointer.ContextItem, Status = PointerStatus.Pending, - StepName = def.Steps.First(x => x.Id == outcomeTarget.NextStep).Name, + StepName = def.Steps.FindById(outcomeTarget.NextStep).Name, Scope = new List(pointer.Scope) }; } @@ -52,7 +52,7 @@ public ExecutionPointer BuildChildPointer(WorkflowDefinition def, ExecutionPoint Active = true, ContextItem = branch, Status = PointerStatus.Pending, - StepName = def.Steps.First(x => x.Id == childDefinitionId).Name, + StepName = def.Steps.FindById(childDefinitionId).Name, Scope = new List(childScope) }; } @@ -68,7 +68,7 @@ public ExecutionPointer BuildCompensationPointer(WorkflowDefinition def, Executi Active = true, ContextItem = pointer.ContextItem, Status = PointerStatus.Pending, - StepName = def.Steps.First(x => x.Id == compensationStepId).Name, + StepName = def.Steps.FindById(compensationStepId).Name, Scope = new List(pointer.Scope) }; } diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index 9c8bad6d9..cdf0a9b42 100755 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -109,7 +109,7 @@ public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition de while (queue.Count > 0) { var exceptionPointer = queue.Dequeue(); - var exceptionStep = def.Steps.Find(x => x.Id == exceptionPointer.StepId); + var exceptionStep = def.Steps.FindById(exceptionPointer.StepId); var compensatingStepId = FindScopeCompensationStepId(workflow, def, exceptionPointer); var errorOption = (exceptionStep.ErrorBehavior ?? (compensatingStepId.HasValue ? WorkflowErrorHandling.Compensate : def.DefaultErrorBehavior)); @@ -129,7 +129,7 @@ public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition de { var pointerId = scope.Pop(); var pointer = workflow.ExecutionPointers.FindById(pointerId); - var step = def.Steps.First(x => x.Id == pointer.StepId); + var step = def.Steps.FindById(pointer.StepId); if (step.CompensationStepId.HasValue) return step.CompensationStepId.Value; } diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index 37ca0cd29..d65262ac9 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -26,6 +26,12 @@ public IStepBuilder Name(string name) return this; } + public IStepBuilder Id(string id) + { + Step.ExternalId = id; + return this; + } + public IStepBuilder Then(Action> stepSetup = null) where TStep : IStepBody { @@ -72,11 +78,23 @@ public IStepBuilder Then(Action bo return stepBuilder; } + public IStepBuilder Attach(string id) + { + Step.Outcomes.Add(new StepOutcome() + { + ExternalNextStepId = id + }); + + return this; + } + public IStepOutcomeBuilder When(object outcomeValue, string label = null) { - StepOutcome result = new StepOutcome(); - result.Value = x => outcomeValue; - result.Label = label; + StepOutcome result = new StepOutcome + { + Value = x => outcomeValue, + Label = label + }; Step.Outcomes.Add(result); var outcomeBuilder = new StepOutcomeBuilder(WorkflowBuilder, result); return outcomeBuilder; diff --git a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs index a98a34185..195231c10 100644 --- a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs @@ -25,20 +25,36 @@ public IWorkflowBuilder UseData() public virtual WorkflowDefinition Build(string id, int version) { - WorkflowDefinition result = new WorkflowDefinition(); - result.Id = id; - result.Version = version; - result.Steps = this.Steps; - result.DefaultErrorBehavior = DefaultErrorBehavior; - result.DefaultErrorRetryInterval = DefaultErrorRetryInterval; - return result; + AttachExternalIds(); + return new WorkflowDefinition + { + Id = id, + Version = version, + Steps = new WorkflowStepCollection(Steps), + DefaultErrorBehavior = DefaultErrorBehavior, + DefaultErrorRetryInterval = DefaultErrorRetryInterval + }; } public void AddStep(WorkflowStep step) { step.Id = Steps.Count(); Steps.Add(step); - } + } + + private void AttachExternalIds() + { + foreach (var step in Steps) + { + foreach (var outcome in step.Outcomes.Where(x => !string.IsNullOrEmpty(x.ExternalNextStepId))) + { + if (Steps.All(x => x.ExternalId != outcome.ExternalNextStepId)) + throw new KeyNotFoundException($"Cannot find step id {outcome.ExternalNextStepId}"); + + outcome.NextStep = Steps.Single(x => x.ExternalId == outcome.ExternalNextStepId).Id; + } + } + } } diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 45716b83d..b31debc3b 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -54,7 +54,7 @@ public async Task Execute(WorkflowInstance workflow) if (pointer.Status == PointerStatus.Cancelled) continue; - var step = def.Steps.First(x => x.Id == pointer.StepId); + var step = def.Steps.FindById(pointer.StepId); if (step != null) { try @@ -186,7 +186,7 @@ private void ProcessAfterExecutionIteration(WorkflowInstance workflow, WorkflowD foreach (var pointer in pointers) { - var step = workflowDef.Steps.First(x => x.Id == pointer.StepId); + var step = workflowDef.Steps.FindById(pointer.StepId); step?.AfterWorkflowIteration(workflowResult, workflowDef, workflow, pointer); } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 55b2e2337..df3e383f0 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.8.2 - 1.8.2.0 - 1.8.2.0 + 1.8.3 + 1.8.3.0 + 1.8.3.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs index 6d52de105..6daed010b 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -15,8 +16,7 @@ public class SQSQueueProvider : IQueueProvider private const int WaitTime = 5; private readonly ILogger _logger; private readonly IAmazonSQS _client; - private string _workflowQueue; - private string _eventQueue; + private readonly Dictionary _queues = new Dictionary(); public bool IsDequeueBlocking => true; @@ -28,32 +28,14 @@ public SQSQueueProvider(AWSCredentials credentials, AmazonSQSConfig config, ILog public async Task QueueWork(string id, QueueType queue) { - var queueUrl = string.Empty; - switch (queue) - { - case QueueType.Workflow: - queueUrl = _workflowQueue; - break; - case QueueType.Event: - queueUrl = _eventQueue; - break; - } + var queueUrl = _queues[queue]; await _client.SendMessageAsync(new SendMessageRequest(queueUrl, id)); } public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) { - var queueUrl = string.Empty; - switch (queue) - { - case QueueType.Workflow: - queueUrl = _workflowQueue; - break; - case QueueType.Event: - queueUrl = _eventQueue; - break; - } + var queueUrl = _queues[queue]; var result = await _client.ReceiveMessageAsync(new ReceiveMessageRequest(queueUrl) { @@ -74,9 +56,11 @@ public async Task Start() { var workflowQueue = await _client.CreateQueueAsync(new CreateQueueRequest("workflowcore-workflows")); var eventQueue = await _client.CreateQueueAsync(new CreateQueueRequest("workflowcore-events")); + var indexQueue = await _client.CreateQueueAsync(new CreateQueueRequest("workflowcore-index")); - _workflowQueue = workflowQueue.QueueUrl; - _eventQueue = eventQueue.QueueUrl; + _queues[QueueType.Workflow] = workflowQueue.QueueUrl; + _queues[QueueType.Event] = eventQueue.QueueUrl; + _queues[QueueType.Index] = indexQueue.QueueUrl; } public Task Stop() => Task.CompletedTask; diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 70c6896ef..b7e3ddddc 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,8 +11,8 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 1.8.1 - 1.8.1.0 + 1.8.2 + 1.8.2.0 diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs index 4c297a8ee..5b104ab30 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs @@ -13,8 +13,8 @@ namespace WorkflowCore.Providers.Azure.Services public class AzureStorageQueueProvider : IQueueProvider { private readonly ILogger _logger; - private readonly CloudQueue _workflowQueue; - private readonly CloudQueue _eventQueue; + + private readonly Dictionary _queues = new Dictionary(); public bool IsDequeueBlocking => false; @@ -24,37 +24,20 @@ public AzureStorageQueueProvider(string connectionString, ILoggerFactory logFact var account = CloudStorageAccount.Parse(connectionString); var client = account.CreateCloudQueueClient(); - _workflowQueue = client.GetQueueReference("workflowcore-workflows"); - _eventQueue = client.GetQueueReference("workflowcore-events"); + _queues[QueueType.Workflow] = client.GetQueueReference("workflowcore-workflows"); + _queues[QueueType.Event] = client.GetQueueReference("workflowcore-events"); + _queues[QueueType.Index] = client.GetQueueReference("workflowcore-index"); } public async Task QueueWork(string id, QueueType queue) { var msg = new CloudQueueMessage(id); - - switch (queue) - { - case QueueType.Workflow: - await _workflowQueue.AddMessageAsync(msg); - break; - case QueueType.Event: - await _eventQueue.AddMessageAsync(msg); - break; - } + await _queues[queue].AddMessageAsync(msg); } public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) { - CloudQueue cloudQueue = null; - switch (queue) - { - case QueueType.Workflow: - cloudQueue = _workflowQueue; - break; - case QueueType.Event: - cloudQueue = _eventQueue; - break; - } + CloudQueue cloudQueue = _queues[queue]; if (cloudQueue == null) return null; @@ -70,8 +53,10 @@ public async Task DequeueWork(QueueType queue, CancellationToken cancell public async Task Start() { - await _workflowQueue.CreateIfNotExistsAsync(); - await _eventQueue.CreateIfNotExistsAsync(); + foreach (var queue in _queues.Values) + { + await queue.CreateIfNotExistsAsync(); + } } public Task Stop() => Task.CompletedTask; diff --git a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj index f07725052..b5e058c85 100644 --- a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj +++ b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj @@ -7,15 +7,15 @@ - Provides distributed lock management on Workflow Core - Provides Queueing support on Workflow Core workflow workflowcore dlm - 1.7.0 + 1.8.2 $(PackageTargetFallback);dnxcore50 https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git Daniel Gerlag - 1.7.0.0 - 1.7.0.0 + 1.8.2.0 + 1.8.2.0 diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs index fdfb50240..dbed9fa98 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs @@ -14,11 +14,17 @@ public class RedisQueueProvider : IQueueProvider private readonly ILogger _logger; private readonly string _connectionString; private readonly string _prefix; - private const string WORKFLOW_QUEUE = "workflows"; - private const string EVENT_QUEUE = "events"; + private IConnectionMultiplexer _multiplexer; private IDatabase _redis; + private readonly Dictionary _queues = new Dictionary() + { + [QueueType.Workflow] = "workflows", + [QueueType.Event] = "events", + [QueueType.Index] = "index" + }; + public RedisQueueProvider(string connectionString, string prefix, ILoggerFactory logFactory) { _connectionString = connectionString; @@ -66,20 +72,6 @@ public void Dispose() { } - private string GetQueueName(QueueType queue) - { - var queueName = string.Empty; - switch (queue) - { - case QueueType.Workflow: - queueName = $"{_prefix}-{WORKFLOW_QUEUE}"; - break; - case QueueType.Event: - queueName = $"{_prefix}-{EVENT_QUEUE}"; - break; - } - - return queueName; - } + private string GetQueueName(QueueType queue) => $"{_prefix}-{_queues[queue]}"; } } diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index 910b79be1..bb0169a34 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.8.0 + 1.8.2 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs index c09c63166..c28091092 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs @@ -96,6 +96,8 @@ private string GetQueueName(QueueType queue) return "wfc.workflow_queue"; case QueueType.Event: return "wfc.event_queue"; + case QueueType.Index: + return "wfc.index_queue"; } return null; } diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj index 5d13307c4..c24f2285c 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj @@ -17,10 +17,10 @@ false false false - 1.7.0 + 1.8.2 Queue provider for Workflow-core using RabbitMQ - 1.7.0.0 - 1.7.0.0 + 1.8.2.0 + 1.8.2.0 diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs index 605b2e8a6..691e6561b 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs @@ -1,6 +1,8 @@ #region using using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using WorkflowCore.Interface; using WorkflowCore.QueueProviders.SqlServer.Interfaces; @@ -15,26 +17,13 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services /// public class QueueConfigProvider : IQueueConfigProvider { - private readonly QueueConfig _workflowQueueConfig; - private readonly QueueConfig _eventQueueConfig; - - public QueueConfigProvider() - { - _workflowQueueConfig = new QueueConfig("workflow"); - _eventQueueConfig = new QueueConfig("event"); - } - - public QueueConfig GetByQueue(QueueType queue) + private readonly Dictionary _queues = new Dictionary() { - switch (queue) - { - case QueueType.Workflow: - return _workflowQueueConfig; - case QueueType.Event: - return _eventQueueConfig; - default: - throw new ArgumentOutOfRangeException(nameof(queue), queue, null); - } - } + [QueueType.Workflow] = new QueueConfig("workflow"), + [QueueType.Event] = new QueueConfig("event"), + [QueueType.Index] = new QueueConfig("indexq") + }; + + public QueueConfig GetByQueue(QueueType queue) => _queues[queue]; } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index 7853d7800..46e91b79f 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -40,7 +40,8 @@ public void MigrateDb() var queueConfigurations = new[] { _configProvider.GetByQueue(QueueType.Workflow), - _configProvider.GetByQueue(QueueType.Event) + _configProvider.GetByQueue(QueueType.Event), + _configProvider.GetByQueue(QueueType.Index) }; foreach (var item in queueConfigurations) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 6ef3bf079..8b600c89a 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -5,7 +5,7 @@ Roberto Paterlini Queue provider for Workflow-core using SQL Server Service Broker - 1.0.1-alpha + 1.0.2-alpha diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/AttachScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/AttachScenario.cs new file mode 100644 index 000000000..9f7f93245 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/AttachScenario.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using System.Threading; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class AttachScenario : WorkflowTest + { + internal static int Step1Ticker = 0; + internal static int Step2Ticker = 0; + + public class MyDataClass + { + } + + public class GotoWorkflow : IWorkflow + { + public string Id => "GotoWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => + { + Step1Ticker++; + return ExecutionResult.Next(); + }) + .Id("step1") + .If(data => Step1Ticker < 4).Do(then => then + .StartWith(context => + { + Step2Ticker++; + return ExecutionResult.Next(); + }) + .Attach("step1") + ); + } + } + + public AttachScenario() + { + Setup(); + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(new MyDataClass()); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + Step1Ticker.Should().Be(4); + Step2Ticker.Should().Be(3); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + } + } +} diff --git a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs index fc92a874c..99a9b49ce 100644 --- a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs +++ b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs @@ -63,8 +63,8 @@ public void ParseDefinitionDynamic() private bool MatchTestDefinition(WorkflowDefinition def) { //TODO: make this better - var step1 = def.Steps.Single(s => s.Tag == "Step1"); - var step2 = def.Steps.Single(s => s.Tag == "Step2"); + var step1 = def.Steps.Single(s => s.ExternalId == "Step1"); + var step2 = def.Steps.Single(s => s.ExternalId == "Step2"); step1.Outcomes.Count.Should().Be(1); step1.Inputs.Count.Should().Be(1); diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 7597532d5..fec58c547 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -324,7 +324,7 @@ private void Given1StepWorkflow(WorkflowStep step1, string id, int version) Id = id, Version = version, DataType = typeof(object), - Steps = new List() + Steps = new WorkflowStepCollection() { step1 } From 03fd813e824c0dd34817f95a6fd04907f8a9fc62 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 26 Feb 2019 13:06:09 -0800 Subject: [PATCH 109/462] Update SingleNodeQueueProvider.cs --- .../Services/DefaultProviders/SingleNodeQueueProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs b/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs index 38a8c5c4f..4984308c6 100644 --- a/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs @@ -21,7 +21,7 @@ public class SingleNodeQueueProvider : IQueueProvider [QueueType.Index] = new BlockingCollection() }; - public bool IsDequeueBlocking => true; + public bool IsDequeueBlocking => false; public async Task QueueWork(string id, QueueType queue) { @@ -30,7 +30,7 @@ public async Task QueueWork(string id, QueueType queue) public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) { - if (_queues[queue].TryTake(out string id, 100, cancellationToken)) + if (_queues[queue].TryTake(out string id)) return id; return null; From 7177e4f90079dbaeb5912f8f96a0ab5910a40db9 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 26 Feb 2019 13:06:29 -0800 Subject: [PATCH 110/462] Update WorkflowCore.csproj --- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index df3e383f0..2cee6c984 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.8.3 - 1.8.3.0 - 1.8.3.0 + 1.8.4 + 1.8.4.0 + 1.8.4.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From 5819629c736fdeadf9ced740e973dcc67747175c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 3 Mar 2019 09:43:45 -0800 Subject: [PATCH 111/462] Async index (#272) --- ReleaseNotes/1.9.0.md | 6 ++ WorkflowCore.sln | 1 + src/WorkflowCore/Models/WorkflowOptions.cs | 4 +- .../ServiceCollectionExtensions.cs | 2 + .../Services/BackgroundTasks/EventConsumer.cs | 2 +- .../Services/BackgroundTasks/IndexConsumer.cs | 89 +++++++++++++++++++ .../Services/BackgroundTasks/QueueConsumer.cs | 55 ++++++++---- .../BackgroundTasks/WorkflowConsumer.cs | 6 +- .../MemoryPersistenceProvider.cs | 34 +++---- .../SingleNodeQueueProvider.cs | 15 ++-- .../TransientMemoryPersistenceProvider.cs | 51 +++++++++++ .../Services/WorkflowController.cs | 12 ++- src/WorkflowCore/WorkflowCore.csproj | 8 +- .../WorkflowCore.Providers.AWS.csproj | 4 +- .../WorkflowCore.Providers.Azure.csproj | 6 +- .../Services/ElasticsearchIndexer.cs | 24 ++--- ...orkflowCore.Providers.Elasticsearch.csproj | 2 +- .../WorkflowCore.Providers.Redis.csproj | 2 +- ...orkflowCore.QueueProviders.RabbitMQ.csproj | 6 +- ...rkflowCore.QueueProviders.SqlServer.csproj | 2 +- .../WorkflowCore.Testing.csproj | 6 +- test/WorkflowCore.Testing/WorkflowTest.cs | 2 +- .../MemoryPersistenceProviderFixture.cs | 4 +- 23 files changed, 249 insertions(+), 94 deletions(-) create mode 100644 ReleaseNotes/1.9.0.md create mode 100644 src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs create mode 100644 src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs diff --git a/ReleaseNotes/1.9.0.md b/ReleaseNotes/1.9.0.md new file mode 100644 index 000000000..fcb0b3c50 --- /dev/null +++ b/ReleaseNotes/1.9.0.md @@ -0,0 +1,6 @@ +# Workflow Core 1.9.0 + +* Indexing is now asynchronous with some simple retry mechanisms to deal with transient errors +* Removed global static state from `MemoryPersistenceProvider` +* Removed dependency on data flow library +* Optimized background queue consumers (workflow, event and index) dispatching task \ No newline at end of file diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 181566fd6..02433997b 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -97,6 +97,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.7.0.md = ReleaseNotes\1.7.0.md ReleaseNotes\1.8.0.md = ReleaseNotes\1.8.0.md ReleaseNotes\1.8.1.md = ReleaseNotes\1.8.1.md + ReleaseNotes\1.9.0.md = ReleaseNotes\1.9.0.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index 16d524b99..dd8f0cec3 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -24,11 +24,11 @@ public WorkflowOptions(IServiceCollection services) Services = services; PollInterval = TimeSpan.FromSeconds(10); IdleTime = TimeSpan.FromMilliseconds(100); - ErrorRetryInterval = TimeSpan.FromSeconds(60); + ErrorRetryInterval = TimeSpan.FromSeconds(60); QueueFactory = new Func(sp => new SingleNodeQueueProvider()); LockFactory = new Func(sp => new SingleNodeLockProvider()); - PersistanceFactory = new Func(sp => new MemoryPersistenceProvider()); + PersistanceFactory = new Func(sp => new TransientMemoryPersistenceProvider(sp.GetService())); SearchIndexFactory = new Func(sp => new NullSearchIndex()); EventHubFactory = new Func(sp => new SingleNodeEventHub(sp.GetService())); } diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 1d10c5742..e6125d2c6 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -24,6 +24,7 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A var options = new WorkflowOptions(services); setupAction?.Invoke(options); + services.AddSingleton(); services.AddTransient(options.PersistanceFactory); services.AddSingleton(options.QueueFactory); services.AddSingleton(options.LockFactory); @@ -36,6 +37,7 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(sp => sp.GetService()); diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index 24b700267..a5e1bc527 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -13,7 +13,7 @@ internal class EventConsumer : QueueConsumer, IBackgroundTask private readonly IPersistenceProvider _persistenceStore; private readonly IDistributedLockProvider _lockProvider; private readonly IDateTimeProvider _datetimeProvider; - + protected override int MaxConcurrentItems => 2; protected override QueueType Queue => QueueType.Event; public EventConsumer(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, WorkflowOptions options, IDateTimeProvider datetimeProvider) diff --git a/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs new file mode 100644 index 000000000..661fad789 --- /dev/null +++ b/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ObjectPool; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Services.BackgroundTasks +{ + internal class IndexConsumer : QueueConsumer, IBackgroundTask + { + private readonly ISearchIndex _searchIndex; + private readonly ObjectPool _persistenceStorePool; + private readonly ILogger _logger; + private readonly Dictionary _errorCounts = new Dictionary(); + + protected override QueueType Queue => QueueType.Index; + protected override bool EnableSecondPasses => true; + + public IndexConsumer(IPooledObjectPolicy persistencePoolPolicy, IQueueProvider queueProvider, ILoggerFactory loggerFactory, ISearchIndex searchIndex, WorkflowOptions options) + : base(queueProvider, loggerFactory, options) + { + _persistenceStorePool = new DefaultObjectPool(persistencePoolPolicy); + _searchIndex = searchIndex; + _logger = loggerFactory.CreateLogger(GetType()); + } + + protected override async Task ProcessItem(string itemId, CancellationToken cancellationToken) + { + try + { + var workflow = await FetchWorkflow(itemId); + await _searchIndex.IndexWorkflow(workflow); + lock (_errorCounts) + { + _errorCounts.Remove(itemId); + } + } + catch (Exception e) + { + Logger.LogWarning(default(EventId), $"Error indexing workfow - {itemId} - {e.Message}"); + var errCount = 0; + lock (_errorCounts) + { + if (!_errorCounts.ContainsKey(itemId)) + _errorCounts.Add(itemId, 0); + + _errorCounts[itemId]++; + errCount = _errorCounts[itemId]; + } + + if (errCount < 5) + { + await QueueProvider.QueueWork(itemId, Queue); + return; + } + if (errCount < 20) + { + await Task.Delay(TimeSpan.FromSeconds(10)); + await QueueProvider.QueueWork(itemId, Queue); + return; + } + + lock (_errorCounts) + { + _errorCounts.Remove(itemId); + } + + Logger.LogError(default(EventId), $"Unable to index workfow - {itemId} - {e.Message}"); + } + } + + private async Task FetchWorkflow(string id) + { + var store = _persistenceStorePool.Get(); + try + { + return await store.GetWorkflowInstance(id); + } + finally + { + _persistenceStorePool.Return(store); + } + } + + } +} diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 1c23bebf6..959ab24b1 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -1,7 +1,7 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using System.Threading.Tasks.Dataflow; using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -12,6 +12,7 @@ internal abstract class QueueConsumer : IBackgroundTask { protected abstract QueueType Queue { get; } protected virtual int MaxConcurrentItems => Math.Max(Environment.ProcessorCount, 2); + protected virtual bool EnableSecondPasses => false; protected readonly IQueueProvider QueueProvider; protected readonly ILogger Logger; @@ -37,7 +38,7 @@ public virtual void Start() _cancellationTokenSource = new CancellationTokenSource(); - DispatchTask = new Task(Execute); + DispatchTask = new Task(Execute, TaskCreationOptions.LongRunning); DispatchTask.Start(); } @@ -51,20 +52,16 @@ public virtual void Stop() private async void Execute() { var cancelToken = _cancellationTokenSource.Token; - var opts = new ExecutionDataflowBlockOptions() - { - MaxDegreeOfParallelism = MaxConcurrentItems, - BoundedCapacity = MaxConcurrentItems + 1 - }; - - var actionBlock = new ActionBlock(ExecuteItem, opts); + var activeTasks = new Dictionary(); + var secondPasses = new HashSet(); while (!cancelToken.IsCancellationRequested) { try { - if (!SpinWait.SpinUntil(() => actionBlock.InputCount == 0, Options.IdleTime)) + if (activeTasks.Count >= MaxConcurrentItems) { + await Task.Delay(Options.IdleTime); continue; } @@ -76,11 +73,40 @@ private async void Execute() await Task.Delay(Options.IdleTime, cancelToken); continue; } + + if (activeTasks.ContainsKey(item)) + { + secondPasses.Add(item); + continue; + } + + secondPasses.Remove(item); - if (!actionBlock.Post(item)) + var task = new Task(async (object data) => + { + try + { + await ExecuteItem((string)data); + while (EnableSecondPasses && secondPasses.Contains(item)) + { + secondPasses.Remove(item); + await ExecuteItem((string)data); + } + } + finally + { + lock (activeTasks) + { + activeTasks.Remove((string)data); + } + } + }, item); + lock (activeTasks) { - await QueueProvider.QueueWork(item, Queue); + activeTasks.Add(item, task); } + + task.Start(); } catch (OperationCanceledException) { @@ -91,8 +117,7 @@ private async void Execute() } } - actionBlock.Complete(); - await actionBlock.Completion; + await Task.WhenAll(activeTasks.Values); } private async Task ExecuteItem(string itemId) @@ -107,7 +132,7 @@ private async Task ExecuteItem(string itemId) } catch (Exception ex) { - Logger.LogError($"Error executing item {itemId} - {ex.Message}"); + Logger.LogError(default(EventId), ex, $"Error executing item {itemId} - {ex.Message}"); } } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index dcad34e18..3212a7c28 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -12,19 +12,17 @@ internal class WorkflowConsumer : QueueConsumer, IBackgroundTask { private readonly IDistributedLockProvider _lockProvider; private readonly IDateTimeProvider _datetimeProvider; - private readonly ISearchIndex _searchIndex; private readonly ObjectPool _persistenceStorePool; private readonly ObjectPool _executorPool; protected override QueueType Queue => QueueType.Workflow; - public WorkflowConsumer(IPooledObjectPolicy persistencePoolPolicy, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IPooledObjectPolicy executorPoolPolicy, IDateTimeProvider datetimeProvider, ISearchIndex searchIndex, WorkflowOptions options) + public WorkflowConsumer(IPooledObjectPolicy persistencePoolPolicy, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IPooledObjectPolicy executorPoolPolicy, IDateTimeProvider datetimeProvider, WorkflowOptions options) : base(queueProvider, loggerFactory, options) { _persistenceStorePool = new DefaultObjectPool(persistencePoolPolicy); _executorPool = new DefaultObjectPool(executorPoolPolicy); _lockProvider = lockProvider; - _searchIndex = searchIndex; _datetimeProvider = datetimeProvider; } @@ -52,7 +50,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { _executorPool.Return(executor); await persistenceStore.PersistWorkflow(workflow); - await _searchIndex.IndexWorkflow(workflow); + await QueueProvider.QueueWork(itemId, QueueType.Index); } } } diff --git a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs index 26c14153a..2818fb5f9 100644 --- a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs @@ -8,34 +8,22 @@ namespace WorkflowCore.Services { + + public interface ISingletonMemoryProvider : IPersistenceProvider + { + } #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously /// /// In-memory implementation of IPersistenceProvider for demo and testing purposes /// - public class MemoryPersistenceProvider : IPersistenceProvider - { - private static readonly ConcurrentDictionary, List, List, List>> Environments = - new ConcurrentDictionary, List, List, List>>(); - private readonly List _instances; - private readonly List _subscriptions; - private readonly List _events; - private readonly List _errors; - - public MemoryPersistenceProvider() - : this("") - { - } - - public MemoryPersistenceProvider(string environmentKey) - { - var environment = Environments.GetOrAdd(environmentKey, _ => Tuple.Create(new List(), new List(), new List(), new List())); - _instances = environment.Item1; - _subscriptions = environment.Item2; - _events = environment.Item3; - _errors = environment.Item4; - } - + public class MemoryPersistenceProvider : ISingletonMemoryProvider + { + private readonly List _instances = new List(); + private readonly List _subscriptions = new List(); + private readonly List _events = new List(); + private readonly List _errors = new List(); + public async Task CreateNewWorkflow(WorkflowInstance workflow) { lock (_instances) diff --git a/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs b/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs index 4984308c6..8e0bd8dda 100644 --- a/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs @@ -21,19 +21,20 @@ public class SingleNodeQueueProvider : IQueueProvider [QueueType.Index] = new BlockingCollection() }; - public bool IsDequeueBlocking => false; + public bool IsDequeueBlocking => true; - public async Task QueueWork(string id, QueueType queue) + public Task QueueWork(string id, QueueType queue) { _queues[queue].Add(id); + return Task.CompletedTask; } - public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) - { - if (_queues[queue].TryTake(out string id)) - return id; + public Task DequeueWork(QueueType queue, CancellationToken cancellationToken) + { + if (_queues[queue].TryTake(out string id, 100, cancellationToken)) + return Task.FromResult(id); - return null; + return Task.FromResult(null); } public Task Start() diff --git a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs new file mode 100644 index 000000000..5a2bf288c --- /dev/null +++ b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Services +{ + public class TransientMemoryPersistenceProvider : IPersistenceProvider + { + private readonly ISingletonMemoryProvider _innerService; + + public TransientMemoryPersistenceProvider(ISingletonMemoryProvider innerService) + { + _innerService = innerService; + } + + public Task CreateEvent(Event newEvent) => _innerService.CreateEvent(newEvent); + + public Task CreateEventSubscription(EventSubscription subscription) => _innerService.CreateEventSubscription(subscription); + + public Task CreateNewWorkflow(WorkflowInstance workflow) => _innerService.CreateNewWorkflow(workflow); + + public void EnsureStoreExists() => _innerService.EnsureStoreExists(); + + public Task GetEvent(string id) => _innerService.GetEvent(id); + + public Task> GetEvents(string eventName, string eventKey, DateTime asOf) => _innerService.GetEvents(eventName, eventKey, asOf); + + public Task> GetRunnableEvents(DateTime asAt) => _innerService.GetRunnableEvents(asAt); + + public Task> GetRunnableInstances(DateTime asAt) => _innerService.GetRunnableInstances(asAt); + + public Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) => _innerService.GetSubcriptions(eventName, eventKey, asOf); + + public Task GetWorkflowInstance(string Id) => _innerService.GetWorkflowInstance(Id); + + public Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) => _innerService.GetWorkflowInstances(status, type, createdFrom, createdTo, skip, take); + + public Task MarkEventProcessed(string id) => _innerService.MarkEventProcessed(id); + + public Task MarkEventUnprocessed(string id) => _innerService.MarkEventUnprocessed(id); + + public Task PersistErrors(IEnumerable errors) => _innerService.PersistErrors(errors); + + public Task PersistWorkflow(WorkflowInstance workflow) => _innerService.PersistWorkflow(workflow); + + public Task TerminateSubscription(string eventSubscriptionId) => _innerService.TerminateSubscription(eventSubscriptionId); + } +} diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index f76c3f45d..fc7c7b810 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -19,10 +19,9 @@ public class WorkflowController : IWorkflowController private readonly IQueueProvider _queueProvider; private readonly IExecutionPointerFactory _pointerFactory; private readonly ILifeCycleEventHub _eventHub; - private readonly ISearchIndex _searchIndex; private readonly ILogger _logger; - public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ISearchIndex searchIndex, ILoggerFactory loggerFactory) + public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory) { _persistenceStore = persistenceStore; _lockProvider = lockProvider; @@ -30,7 +29,6 @@ public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLoc _queueProvider = queueProvider; _pointerFactory = pointerFactory; _eventHub = eventHub; - _searchIndex = searchIndex; _logger = loggerFactory.CreateLogger(); } @@ -84,7 +82,7 @@ public async Task StartWorkflow(string workflowId, int? version, string id = await _persistenceStore.CreateNewWorkflow(wf); await _queueProvider.QueueWork(id, QueueType.Workflow); - await _searchIndex.IndexWorkflow(wf); + await _queueProvider.QueueWork(id, QueueType.Index); await _eventHub.PublishNotification(new WorkflowStarted() { EventTimeUtc = DateTime.UtcNow, @@ -127,7 +125,7 @@ public async Task SuspendWorkflow(string workflowId) { wf.Status = WorkflowStatus.Suspended; await _persistenceStore.PersistWorkflow(wf); - await _searchIndex.IndexWorkflow(wf); + await _queueProvider.QueueWork(workflowId, QueueType.Index); await _eventHub.PublishNotification(new WorkflowSuspended() { EventTimeUtc = DateTime.UtcNow, @@ -163,7 +161,7 @@ public async Task ResumeWorkflow(string workflowId) wf.Status = WorkflowStatus.Runnable; await _persistenceStore.PersistWorkflow(wf); requeue = true; - await _searchIndex.IndexWorkflow(wf); + await _queueProvider.QueueWork(workflowId, QueueType.Index); await _eventHub.PublishNotification(new WorkflowResumed() { EventTimeUtc = DateTime.UtcNow, @@ -197,7 +195,7 @@ public async Task TerminateWorkflow(string workflowId) var wf = await _persistenceStore.GetWorkflowInstance(workflowId); wf.Status = WorkflowStatus.Terminated; await _persistenceStore.PersistWorkflow(wf); - await _searchIndex.IndexWorkflow(wf); + await _queueProvider.QueueWork(workflowId, QueueType.Index); await _eventHub.PublishNotification(new WorkflowTerminated() { EventTimeUtc = DateTime.UtcNow, diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 2cee6c984..ecbc04983 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.8.4 - 1.8.4.0 - 1.8.4.0 + 1.9.0 + 1.9.0.0 + 1.9.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png @@ -28,8 +28,8 @@ - + diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index b7e3ddddc..0eed166ea 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,8 +11,8 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 1.8.2 - 1.8.2.0 + 1.9.0 + 1.9.0.0 diff --git a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj index b5e058c85..c8dfff7f9 100644 --- a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj +++ b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj @@ -7,15 +7,15 @@ - Provides distributed lock management on Workflow Core - Provides Queueing support on Workflow Core workflow workflowcore dlm - 1.8.2 + 1.9.0 $(PackageTargetFallback);dnxcore50 https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git Daniel Gerlag - 1.8.2.0 - 1.8.2.0 + 1.9.0.0 + 1.9.0.0 diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs b/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs index 2c3634f38..1c3ee0c83 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs @@ -30,24 +30,18 @@ public async Task IndexWorkflow(WorkflowInstance workflow) { if (_client == null) throw new InvalidOperationException("Not started"); - - try - { - var denormModel = WorkflowSearchModel.FromWorkflowInstance(workflow); + + var denormModel = WorkflowSearchModel.FromWorkflowInstance(workflow); - var result = await _client.IndexAsync(denormModel, idx => idx - .Index(_indexName) - ); + var result = await _client.IndexAsync(denormModel, idx => idx + .Index(_indexName) + ); - if (!result.ApiCall.Success) - { - _logger.LogError(default(EventId), result.ApiCall.OriginalException, $"Failed to index workflow {workflow.Id}"); - } - } - catch (Exception ex) + if (!result.ApiCall.Success) { - _logger.LogError(default(EventId), ex, $"Failed to index workflow {workflow.Id}"); - } + _logger.LogError(default(EventId), result.ApiCall.OriginalException, $"Failed to index workflow {workflow.Id}"); + throw new ApplicationException($"Failed to index workflow {workflow.Id}", result.ApiCall.OriginalException); + } } public async Task> Search(string terms, int skip, int take, params SearchFilter[] filters) diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index a1791afa6..a38d3dda5 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.8.0 + 1.9.0 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index bb0169a34..4c5ec6550 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.8.2 + 1.9.0 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj index c24f2285c..8cb58ca42 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj @@ -17,10 +17,10 @@ false false false - 1.8.2 + 1.9.0 Queue provider for Workflow-core using RabbitMQ - 1.8.2.0 - 1.8.2.0 + 1.9.0.0 + 1.9.0.0 diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 8b600c89a..131a20359 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -5,7 +5,7 @@ Roberto Paterlini Queue provider for Workflow-core using SQL Server Service Broker - 1.0.2-alpha + 1.0.3-alpha diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 9d866621b..dd02cc70d 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 1.8.0 - 1.8.0.0 - 1.7.0.0 + 1.9.0 + 1.9.0.0 + 1.9.0.0 Facilitates testing of workflows built on Workflow-Core diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/test/WorkflowCore.Testing/WorkflowTest.cs index 9c7d0dacc..4c9602148 100644 --- a/test/WorkflowCore.Testing/WorkflowTest.cs +++ b/test/WorkflowCore.Testing/WorkflowTest.cs @@ -18,7 +18,7 @@ public abstract class WorkflowTest : IDisposable protected IWorkflowHost Host; protected IPersistenceProvider PersistenceProvider; protected List UnhandledStepErrors = new List(); - + protected virtual void Setup() { //setup dependency injection diff --git a/test/WorkflowCore.UnitTests/Services/MemoryPersistenceProviderFixture.cs b/test/WorkflowCore.UnitTests/Services/MemoryPersistenceProviderFixture.cs index 01e7b7461..f340f3010 100644 --- a/test/WorkflowCore.UnitTests/Services/MemoryPersistenceProviderFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/MemoryPersistenceProviderFixture.cs @@ -8,6 +8,8 @@ namespace WorkflowCore.UnitTests.Services { public class MemoryPersistenceProviderFixture : BasePersistenceFixture { - protected override IPersistenceProvider Subject => new MemoryPersistenceProvider(); + private readonly IPersistenceProvider _subject = new MemoryPersistenceProvider(); + + protected override IPersistenceProvider Subject => _subject; } } From cd77cd8ba4c969c1ffaf55767370854bf6b0fb7c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 3 Mar 2019 10:36:28 -0800 Subject: [PATCH 112/462] fix flakey RetrySagaWithUserTaskScenario test on slower build agents --- .../Scenarios/RetrySagaWithUserTaskScenario.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaWithUserTaskScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaWithUserTaskScenario.cs index cf397fd8c..ccd67a7b8 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaWithUserTaskScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaWithUserTaskScenario.cs @@ -139,7 +139,7 @@ private static async Task> WaitForUserStepAs var openActions = instance.GetOpenUserActions()?.ToList(); while ((openActions?.Count ?? 0) == 0) { - await Task.Delay(TimeSpan.FromMilliseconds(10)); + await Task.Delay(TimeSpan.FromMilliseconds(100)); openActions = instance.GetOpenUserActions()?.ToList(); if (delayCount-- == 0) { From 82c0f1c00d4b5de57bc43430586ef2f0f08d0230 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 5 Mar 2019 21:15:17 -0800 Subject: [PATCH 113/462] readability --- .../Services/BackgroundTasks/EventConsumer.cs | 93 +++++---- .../BackgroundTasks/WorkflowConsumer.cs | 75 ++++---- src/WorkflowCore/Services/WorkflowExecutor.cs | 181 +++++++++--------- 3 files changed, 173 insertions(+), 176 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index a5e1bc527..ff49621e3 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -26,74 +26,71 @@ public EventConsumer(IPersistenceProvider persistenceStore, IQueueProvider queue protected override async Task ProcessItem(string itemId, CancellationToken cancellationToken) { - if (await _lockProvider.AcquireLock($"evt:{itemId}", cancellationToken)) + if (!await _lockProvider.AcquireLock($"evt:{itemId}", cancellationToken)) { - try + Logger.LogInformation($"Event locked {itemId}"); + return; + } + + try + { + cancellationToken.ThrowIfCancellationRequested(); + var evt = await _persistenceStore.GetEvent(itemId); + if (evt.EventTime <= _datetimeProvider.Now.ToUniversalTime()) { - cancellationToken.ThrowIfCancellationRequested(); - var evt = await _persistenceStore.GetEvent(itemId); - if (evt.EventTime <= _datetimeProvider.Now.ToUniversalTime()) - { - var subs = await _persistenceStore.GetSubcriptions(evt.EventName, evt.EventKey, evt.EventTime); - var success = true; + var subs = await _persistenceStore.GetSubcriptions(evt.EventName, evt.EventKey, evt.EventTime); + var success = true; - foreach (var sub in subs.ToList()) - { - success = success && await SeedSubscription(evt, sub, cancellationToken); - } + foreach (var sub in subs.ToList()) + { + success = success && await SeedSubscription(evt, sub, cancellationToken); + } - if (success) - { - await _persistenceStore.MarkEventProcessed(itemId); - } + if (success) + { + await _persistenceStore.MarkEventProcessed(itemId); } } - finally - { - await _lockProvider.ReleaseLock($"evt:{itemId}"); - } } - else + finally { - Logger.LogInformation($"Event locked {itemId}"); + await _lockProvider.ReleaseLock($"evt:{itemId}"); } } private async Task SeedSubscription(Event evt, EventSubscription sub, CancellationToken cancellationToken) { - if (await _lockProvider.AcquireLock(sub.WorkflowId, cancellationToken)) + if (!await _lockProvider.AcquireLock(sub.WorkflowId, cancellationToken)) { - try - { - var workflow = await _persistenceStore.GetWorkflowInstance(sub.WorkflowId); - var pointers = workflow.ExecutionPointers.Where(p => p.EventName == sub.EventName && p.EventKey == sub.EventKey && !p.EventPublished && p.EndTime == null); - foreach (var p in pointers) - { - p.EventData = evt.EventData; - p.EventPublished = true; - p.Active = true; - } - workflow.NextExecution = 0; - await _persistenceStore.PersistWorkflow(workflow); - await _persistenceStore.TerminateSubscription(sub.Id); - return true; - } - catch (Exception ex) - { - Logger.LogError(ex.Message); - return false; - } - finally + Logger.LogInformation("Workflow locked {0}", sub.WorkflowId); + return false; + } + + try + { + var workflow = await _persistenceStore.GetWorkflowInstance(sub.WorkflowId); + var pointers = workflow.ExecutionPointers.Where(p => p.EventName == sub.EventName && p.EventKey == sub.EventKey && !p.EventPublished && p.EndTime == null); + foreach (var p in pointers) { - await _lockProvider.ReleaseLock(sub.WorkflowId); - await QueueProvider.QueueWork(sub.WorkflowId, QueueType.Workflow); + p.EventData = evt.EventData; + p.EventPublished = true; + p.Active = true; } + workflow.NextExecution = 0; + await _persistenceStore.PersistWorkflow(workflow); + await _persistenceStore.TerminateSubscription(sub.Id); + return true; } - else + catch (Exception ex) { - Logger.LogInformation("Workflow locked {0}", sub.WorkflowId); + Logger.LogError(ex.Message); return false; } + finally + { + await _lockProvider.ReleaseLock(sub.WorkflowId); + await QueueProvider.QueueWork(sub.WorkflowId, QueueType.Workflow); + } } } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 3212a7c28..1ff0f67fe 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -28,61 +28,60 @@ public WorkflowConsumer(IPooledObjectPolicy persistencePoo protected override async Task ProcessItem(string itemId, CancellationToken cancellationToken) { - if (await _lockProvider.AcquireLock(itemId, cancellationToken)) + if (!await _lockProvider.AcquireLock(itemId, cancellationToken)) + { + Logger.LogInformation("Workflow locked {0}", itemId); + return; + } + + WorkflowInstance workflow = null; + WorkflowExecutorResult result = null; + var persistenceStore = _persistenceStorePool.Get(); + try { - WorkflowInstance workflow = null; - WorkflowExecutorResult result = null; - var persistenceStore = _persistenceStorePool.Get(); try { - try + cancellationToken.ThrowIfCancellationRequested(); + workflow = await persistenceStore.GetWorkflowInstance(itemId); + if (workflow.Status == WorkflowStatus.Runnable) { - cancellationToken.ThrowIfCancellationRequested(); - workflow = await persistenceStore.GetWorkflowInstance(itemId); - if (workflow.Status == WorkflowStatus.Runnable) + var executor = _executorPool.Get(); + try + { + result = await executor.Execute(workflow); + } + finally { - var executor = _executorPool.Get(); - try - { - result = await executor.Execute(workflow); - } - finally - { - _executorPool.Return(executor); - await persistenceStore.PersistWorkflow(workflow); - await QueueProvider.QueueWork(itemId, QueueType.Index); - } + _executorPool.Return(executor); + await persistenceStore.PersistWorkflow(workflow); + await QueueProvider.QueueWork(itemId, QueueType.Index); } } - finally + } + finally + { + await _lockProvider.ReleaseLock(itemId); + if ((workflow != null) && (result != null)) { - await _lockProvider.ReleaseLock(itemId); - if ((workflow != null) && (result != null)) + foreach (var sub in result.Subscriptions) { - foreach (var sub in result.Subscriptions) - { - await SubscribeEvent(sub, persistenceStore); - } + await SubscribeEvent(sub, persistenceStore); + } - await persistenceStore.PersistErrors(result.Errors); + await persistenceStore.PersistErrors(result.Errors); - var readAheadTicks = _datetimeProvider.Now.Add(Options.PollInterval).ToUniversalTime().Ticks; + var readAheadTicks = _datetimeProvider.Now.Add(Options.PollInterval).ToUniversalTime().Ticks; - if ((workflow.Status == WorkflowStatus.Runnable) && workflow.NextExecution.HasValue && workflow.NextExecution.Value < readAheadTicks) - { - new Task(() => FutureQueue(workflow, cancellationToken)).Start(); - } + if ((workflow.Status == WorkflowStatus.Runnable) && workflow.NextExecution.HasValue && workflow.NextExecution.Value < readAheadTicks) + { + new Task(() => FutureQueue(workflow, cancellationToken)).Start(); } } } - finally - { - _persistenceStorePool.Return(persistenceStore); - } } - else + finally { - Logger.LogInformation("Workflow locked {0}", itemId); + _persistenceStorePool.Return(persistenceStore); } } diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index b31debc3b..46f05d4f7 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -55,123 +55,124 @@ public async Task Execute(WorkflowInstance workflow) continue; var step = def.Steps.FindById(pointer.StepId); - if (step != null) + if (step == null) { - try - { - switch (step.InitForExecution(wfResult, def, workflow, pointer)) + _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); + pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); + wfResult.Errors.Add(new ExecutionError() + { + WorkflowId = workflow.Id, + ExecutionPointerId = pointer.Id, + ErrorTime = _datetimeProvider.Now.ToUniversalTime(), + Message = String.Format("Unable to find step {0} in workflow definition", pointer.StepId) + }); + continue; + } + + try + { + switch (step.InitForExecution(wfResult, def, workflow, pointer)) + { + case ExecutionPipelineDirective.Defer: + continue; + case ExecutionPipelineDirective.EndWorkflow: + workflow.Status = WorkflowStatus.Complete; + workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); + continue; + } + + if (pointer.Status != PointerStatus.Running) + { + pointer.Status = PointerStatus.Running; + _publisher.PublishNotification(new StepStarted() { - case ExecutionPipelineDirective.Defer: - continue; - case ExecutionPipelineDirective.EndWorkflow: - workflow.Status = WorkflowStatus.Complete; - workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); - continue; - } + EventTimeUtc = _datetimeProvider.Now, + Reference = workflow.Reference, + ExecutionPointerId = pointer.Id, + StepId = step.Id, + WorkflowInstanceId = workflow.Id, + WorkflowDefinitionId = workflow.WorkflowDefinitionId, + Version = workflow.Version + }); + } + + if (!pointer.StartTime.HasValue) + { + pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); + } + + using (var scope = _scopeProvider.CreateScope()) + { + _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); + + IStepBody body = step.ConstructBody(scope.ServiceProvider); - if (pointer.Status != PointerStatus.Running) + if (body == null) { - pointer.Status = PointerStatus.Running; - _publisher.PublishNotification(new StepStarted() + _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); + pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); + wfResult.Errors.Add(new ExecutionError() { - EventTimeUtc = _datetimeProvider.Now, - Reference = workflow.Reference, + WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, - StepId = step.Id, - WorkflowInstanceId = workflow.Id, - WorkflowDefinitionId = workflow.WorkflowDefinitionId, - Version = workflow.Version + ErrorTime = _datetimeProvider.Now.ToUniversalTime(), + Message = String.Format("Unable to construct step body {0}", step.BodyType.ToString()) }); + continue; } - if (!pointer.StartTime.HasValue) + IStepExecutionContext context = new StepExecutionContext() { - pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); - } + Workflow = workflow, + Step = step, + PersistenceData = pointer.PersistenceData, + ExecutionPointer = pointer, + Item = pointer.ContextItem + }; - using (var scope = _scopeProvider.CreateScope()) - { - _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); + foreach (var input in step.Inputs) + input.AssignInput(workflow.Data, body, context); - IStepBody body = step.ConstructBody(scope.ServiceProvider); - if (body == null) - { - _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); - pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); - wfResult.Errors.Add(new ExecutionError() - { - WorkflowId = workflow.Id, - ExecutionPointerId = pointer.Id, - ErrorTime = _datetimeProvider.Now.ToUniversalTime(), - Message = String.Format("Unable to construct step body {0}", step.BodyType.ToString()) - }); + switch (step.BeforeExecute(wfResult, context, pointer, body)) + { + case ExecutionPipelineDirective.Defer: continue; - } - - IStepExecutionContext context = new StepExecutionContext() - { - Workflow = workflow, - Step = step, - PersistenceData = pointer.PersistenceData, - ExecutionPointer = pointer, - Item = pointer.ContextItem - }; - - foreach (var input in step.Inputs) - input.AssignInput(workflow.Data, body, context); - - - switch (step.BeforeExecute(wfResult, context, pointer, body)) - { - case ExecutionPipelineDirective.Defer: - continue; - case ExecutionPipelineDirective.EndWorkflow: - workflow.Status = WorkflowStatus.Complete; - workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); - continue; - } - - var result = await body.RunAsync(context); + case ExecutionPipelineDirective.EndWorkflow: + workflow.Status = WorkflowStatus.Complete; + workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); + continue; + } - if (result.Proceed) - { - foreach (var output in step.Outputs) - output.AssignOutput(workflow.Data, body, context); - } + var result = await body.RunAsync(context); - _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); - step.AfterExecute(wfResult, context, result, pointer); - } - } - catch (Exception ex) - { - _logger.LogError("Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); - wfResult.Errors.Add(new ExecutionError() + if (result.Proceed) { - WorkflowId = workflow.Id, - ExecutionPointerId = pointer.Id, - ErrorTime = _datetimeProvider.Now.ToUniversalTime(), - Message = ex.Message - }); - - _executionResultProcessor.HandleStepException(workflow, def, pointer, step, ex); - Host.ReportStepError(workflow, step, ex); + foreach (var output in step.Outputs) + output.AssignOutput(workflow.Data, body, context); + } + + _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); + step.AfterExecute(wfResult, context, result, pointer); } - _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); } - else + catch (Exception ex) { - _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); - pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); + _logger.LogError("Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), - Message = String.Format("Unable to find step {0} in workflow definition", pointer.StepId) + Message = ex.Message }); + + _executionResultProcessor.HandleStepException(workflow, def, pointer, step, ex); + Host.ReportStepError(workflow, step, ex); } + _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); + + } ProcessAfterExecutionIteration(workflow, def, wfResult); From 56611a4f62379b584675a5bc02f11b3eef8f53d1 Mon Sep 17 00:00:00 2001 From: Demian Marty Date: Thu, 7 Mar 2019 21:17:57 +0100 Subject: [PATCH 114/462] add support to get multiple workflow instances by id #261 --- .../Interface/IPersistenceProvider.cs | 4 +- .../MemoryPersistenceProvider.cs | 27 +++++++--- .../TransientMemoryPersistenceProvider.cs | 2 + .../EntityFrameworkPersistenceProvider.cs | 28 ++++++++-- .../Services/MongoPersistenceProvider.cs | 31 +++++++---- .../Services/DynamoPersistenceProvider.cs | 54 ++++++++++++++++++- .../Services/RedisPersistenceProvider.cs | 12 +++++ 7 files changed, 134 insertions(+), 24 deletions(-) diff --git a/src/WorkflowCore/Interface/IPersistenceProvider.cs b/src/WorkflowCore/Interface/IPersistenceProvider.cs index a71128f49..58645b495 100644 --- a/src/WorkflowCore/Interface/IPersistenceProvider.cs +++ b/src/WorkflowCore/Interface/IPersistenceProvider.cs @@ -7,7 +7,7 @@ namespace WorkflowCore.Interface { /// /// The implemention of this interface will be responsible for - /// persisiting running workflow instances to a durable store + /// persisiting running workflow instances to a durable store /// public interface IPersistenceProvider { @@ -22,6 +22,8 @@ public interface IPersistenceProvider Task GetWorkflowInstance(string Id); + Task> GetWorkflowInstances(IEnumerable ids); + Task CreateEventSubscription(EventSubscription subscription); Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf); diff --git a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs index 2818fb5f9..7e43079d1 100644 --- a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs @@ -8,22 +8,22 @@ namespace WorkflowCore.Services { - + public interface ISingletonMemoryProvider : IPersistenceProvider { } - #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously /// /// In-memory implementation of IPersistenceProvider for demo and testing purposes /// public class MemoryPersistenceProvider : ISingletonMemoryProvider - { + { private readonly List _instances = new List(); private readonly List _subscriptions = new List(); private readonly List _events = new List(); private readonly List _errors = new List(); - + public async Task CreateNewWorkflow(WorkflowInstance workflow) { lock (_instances) @@ -61,6 +61,19 @@ public async Task GetWorkflowInstance(string Id) } } + public async Task> GetWorkflowInstances(IEnumerable ids) + { + if (ids == null) + { + return new List(); + } + + lock (_instances) + { + return _instances.Where(x => ids.Contains(x.Id, StringComparer.OrdinalIgnoreCase)); + } + } + public async Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) { lock (_instances) @@ -121,7 +134,7 @@ public async Task TerminateSubscription(string eventSubscriptionId) } public void EnsureStoreExists() - { + { } public async Task CreateEvent(Event newEvent) @@ -133,7 +146,7 @@ public async Task CreateEvent(Event newEvent) return newEvent.Id; } } - + public async Task MarkEventProcessed(string id) { lock (_events) @@ -197,5 +210,5 @@ public async Task PersistErrors(IEnumerable errors) } } - #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously } diff --git a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs index 5a2bf288c..599119557 100644 --- a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs @@ -36,6 +36,8 @@ public TransientMemoryPersistenceProvider(ISingletonMemoryProvider innerService) public Task GetWorkflowInstance(string Id) => _innerService.GetWorkflowInstance(Id); + public Task> GetWorkflowInstances(IEnumerable ids) => _innerService.GetWorkflowInstances(ids); + public Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) => _innerService.GetWorkflowInstances(status, type, createdFrom, createdTo, skip, take); public Task MarkEventProcessed(string id) => _innerService.MarkEventProcessed(id); diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs index f04f35a12..4531f92b1 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs @@ -26,7 +26,7 @@ public EntityFrameworkPersistenceProvider(IWorkflowDbContextFactory contextFacto _canCreateDB = canCreateDB; _canMigrateDB = canMigrateDB; } - + public async Task CreateEventSubscription(EventSubscription subscription) { using (var db = ConstructDbContext()) @@ -96,7 +96,7 @@ public async Task> GetWorkflowInstances(WorkflowSt return result; } } - + public async Task GetWorkflowInstance(string Id) { using (var db = ConstructDbContext()) @@ -115,6 +115,26 @@ public async Task GetWorkflowInstance(string Id) } } + public async Task> GetWorkflowInstances(IEnumerable ids) + { + if (ids == null) + { + return new List(); + } + + using (var db = ConstructDbContext()) + { + var uids = ids.Select(i => new Guid(i)); + var raw = db.Set() + .Include(wf => wf.ExecutionPointers) + .ThenInclude(ep => ep.ExtensionAttributes) + .Include(wf => wf.ExecutionPointers) + .Where(x => uids.Contains(x.InstanceId)); + + return (await raw.ToListAsync()).Select(i => i.ToWorkflowInstance()); + } + } + public async Task PersistWorkflow(WorkflowInstance workflow) { using (var db = ConstructDbContext()) @@ -143,7 +163,7 @@ public async Task TerminateSubscription(string eventSubscriptionId) await db.SaveChangesAsync(); } } - + public virtual void EnsureStoreExists() { using (var context = ConstructDbContext()) @@ -283,7 +303,7 @@ public async Task PersistErrors(IEnumerable errors) } } } - + private WorkflowDbContext ConstructDbContext() { return _contextFactory.Build(); diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index bae6f9bcd..cc75ccc1c 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -26,8 +26,8 @@ public MongoPersistenceProvider(IMongoDatabase database) static MongoPersistenceProvider() { BsonClassMap.RegisterClassMap(x => - { - x.MapIdProperty(y => y.Id) + { + x.MapIdProperty(y => y.Id) .SetIdGenerator(new StringObjectIdGenerator()); x.MapProperty(y => y.Data) .SetSerializer(new DataObjectSerializer()); @@ -48,9 +48,9 @@ static MongoPersistenceProvider() .SetIdGenerator(new StringObjectIdGenerator()); x.MapProperty(y => y.EventName); x.MapProperty(y => y.EventKey); - x.MapProperty(y => y.StepId); + x.MapProperty(y => y.StepId); x.MapProperty(y => y.WorkflowId); - x.MapProperty(y => y.SubscribeAsOf); + x.MapProperty(y => y.SubscribeAsOf); }); BsonClassMap.RegisterClassMap(x => @@ -113,7 +113,18 @@ public async Task GetWorkflowInstance(string Id) var result = await WorkflowInstances.FindAsync(x => x.Id == Id); return await result.FirstAsync(); } - + + public async Task> GetWorkflowInstances(IEnumerable ids) + { + if (ids == null) + { + return new List(); + } + + var result = await WorkflowInstances.FindAsync(x => ids.Contains(x.Id, StringComparer.OrdinalIgnoreCase)); + return await result.ToListAsync(); + } + public async Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) { IQueryable result = WorkflowInstances.AsQueryable(); @@ -138,7 +149,7 @@ public async Task CreateEventSubscription(EventSubscription subscription await EventSubscriptions.InsertOneAsync(subscription); return subscription.Id; } - + public async Task TerminateSubscription(string eventSubscriptionId) { await EventSubscriptions.DeleteOneAsync(x => x.Id == eventSubscriptionId); @@ -146,9 +157,9 @@ public async Task TerminateSubscription(string eventSubscriptionId) public void EnsureStoreExists() { - - } - + + } + public async Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) { var query = EventSubscriptions @@ -192,7 +203,7 @@ public async Task> GetEvents(string eventName, string eventK var query = Events .Find(x => x.EventName == eventName && x.EventKey == eventKey && x.EventTime >= asOf) .Project(x => x.Id); - + return await query.ToListAsync(); } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index 1a6c7bf81..585fc226c 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading.Tasks; using WorkflowCore.Providers.AWS.Interface; @@ -86,7 +87,7 @@ public async Task> GetRunnableInstances(DateTime asAt) }, ScanIndexForward = true }; - + var response = await _client.QueryAsync(request); foreach (var item in response.Items) @@ -117,6 +118,55 @@ public async Task GetWorkflowInstance(string Id) return response.Item.ToWorkflowInstance(); } + public async Task> GetWorkflowInstances(IEnumerable ids) + { + if (ids == null) + { + return new List(); + } + + var keys = new Dictionary(); + foreach (var id in ids) + { + keys.Add("id", new AttributeValue { S = id }); + } + + var request = new BatchGetItemRequest + { + RequestItems = new Dictionary() + { + { + $"{_tablePrefix}-{WORKFLOW_TABLE}", + new KeysAndAttributes + { + Keys = new List >() { keys } + } + } + } + }; + + var result = new List>(); + BatchGetItemResponse response; + do + { + // Making request + response = await _client.BatchGetItemAsync(request); + + // Check the response + var responses = response.Responses; // Attribute list in the response. + foreach (var tableResponse in responses) + { + result.AddRange(tableResponse.Value); + } + + // Any unprocessed keys? could happen if you exceed ProvisionedThroughput or some other error. + Dictionary unprocessedKeys = response.UnprocessedKeys; + request.RequestItems = unprocessedKeys; + } while (response.UnprocessedKeys.Count > 0); + + return result.Select(i => i.ToWorkflowInstance()); + } + public async Task CreateEventSubscription(EventSubscription subscription) { subscription.Id = Guid.NewGuid().ToString(); @@ -326,4 +376,4 @@ public void EnsureStoreExists() _provisioner.ProvisionTables().Wait(); } } -} +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index 57f64c4ef..373ac14a6 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -75,6 +76,17 @@ public async Task GetWorkflowInstance(string Id) return JsonConvert.DeserializeObject(raw, _serializerSettings); } + public async Task> GetWorkflowInstances(IEnumerable ids) + { + if (ids == null) + { + return new List(); + } + + var raw = await _redis.HashGetAsync($"{_prefix}.{WORKFLOW_SET}", Array.ConvertAll(ids.ToArray(), x => (RedisValue)x)); + return raw.Select(r => JsonConvert.DeserializeObject(r, _serializerSettings)); + } + public async Task CreateEventSubscription(EventSubscription subscription) { subscription.Id = Guid.NewGuid().ToString(); From c90663b01d4b4850f995df7423609fbe2c236797 Mon Sep 17 00:00:00 2001 From: Demian Marty Date: Tue, 12 Mar 2019 16:05:25 +0100 Subject: [PATCH 115/462] PR review changes, added UT for GetWorkflowInstances --- .../MemoryPersistenceProvider.cs | 2 +- .../Services/MongoPersistenceProvider.cs | 2 +- .../BasePersistenceFixture.cs | 95 +++++++++++++++++-- 3 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs index 7e43079d1..d69984506 100644 --- a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs @@ -70,7 +70,7 @@ public async Task> GetWorkflowInstances(IEnumerabl lock (_instances) { - return _instances.Where(x => ids.Contains(x.Id, StringComparer.OrdinalIgnoreCase)); + return _instances.Where(x => ids.Contains(x.Id)); } } diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index cc75ccc1c..e59c1230a 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -121,7 +121,7 @@ public async Task> GetWorkflowInstances(IEnumerabl return new List(); } - var result = await WorkflowInstances.FindAsync(x => ids.Contains(x.Id, StringComparer.OrdinalIgnoreCase)); + var result = await WorkflowInstances.FindAsync(x => ids.Contains(x.Id)); return await result.ToListAsync(); } diff --git a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs index 14847b1f1..f005aa48b 100644 --- a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs +++ b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; using WorkflowCore.Interface; using WorkflowCore.Models; -using Xunit; -using FluentAssertions; using WorkflowCore.TestAssets; -using System.Threading.Tasks; +using Xunit; namespace WorkflowCore.UnitTests { @@ -50,7 +50,7 @@ public void GetWorkflowInstance_should_retrieve_workflow() NextExecution = 0, Version = 1, WorkflowDefinitionId = "My Workflow", - Reference = "My Reference" + Reference = "My Reference" }; workflow.ExecutionPointers.Add(new ExecutionPointer() { @@ -69,6 +69,89 @@ public void GetWorkflowInstance_should_retrieve_workflow() .Scope.Should().ContainInOrder(workflow.ExecutionPointers.FindById("1").Scope); } + [Fact] + public void GetWorkflowInstances_should_retrieve_workflows() + { + var workflow01 = new WorkflowInstance() + { + Data = new TestData() { Value1 = 7 }, + Description = "My Description", + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Version = 1, + WorkflowDefinitionId = "My Workflow", + Reference = "My Reference" + }; + workflow01.ExecutionPointers.Add(new ExecutionPointer() + { + Id = "1", + Active = true, + StepId = 0, + SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime(), + Scope = new List() { "4", "3", "2", "1" } + }); + var workflowId01 = Subject.CreateNewWorkflow(workflow01).Result; + + var workflow02 = new WorkflowInstance() + { + Data = new TestData() { Value1 = 7 }, + Description = "My Description", + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Version = 1, + WorkflowDefinitionId = "My Workflow", + Reference = "My Reference" + }; + workflow02.ExecutionPointers.Add(new ExecutionPointer() + { + Id = "1", + Active = true, + StepId = 0, + SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime(), + Scope = new List() { "4", "3", "2", "1" } + }); + var workflowId02 = Subject.CreateNewWorkflow(workflow01).Result; + + var workflow03 = new WorkflowInstance() + { + Data = new TestData() { Value1 = 7 }, + Description = "My Description", + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Version = 1, + WorkflowDefinitionId = "My Workflow", + Reference = "My Reference" + }; + workflow03.ExecutionPointers.Add(new ExecutionPointer() + { + Id = "1", + Active = true, + StepId = 0, + SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime(), + Scope = new List() { "4", "3", "2", "1" } + }); + var workflowId03 = Subject.CreateNewWorkflow(workflow01).Result; + + var retrievedWorkflows = Subject.GetWorkflowInstances(new[] { workflowId01, workflowId02, workflowId03 }).Result; + + retrievedWorkflows.Count().ShouldBeEquivalentTo(3); + + var retrievedWorkflow01 = retrievedWorkflows.Single(o => o.Id == workflowId01); + retrievedWorkflow01.ShouldBeEquivalentTo(workflow01); + retrievedWorkflow01.ExecutionPointers.FindById("1") + .Scope.Should().ContainInOrder(workflow01.ExecutionPointers.FindById("1").Scope); + + var retrievedWorkflow02 = retrievedWorkflows.Single(o => o.Id == workflowId02); + retrievedWorkflow02.ShouldBeEquivalentTo(workflow02); + retrievedWorkflow02.ExecutionPointers.FindById("1") + .Scope.Should().ContainInOrder(workflow02.ExecutionPointers.FindById("1").Scope); + + var retrievedWorkflow03 = retrievedWorkflows.Single(o => o.Id == workflowId03); + retrievedWorkflow03.ShouldBeEquivalentTo(workflow03); + retrievedWorkflow03.ExecutionPointers.FindById("1") + .Scope.Should().ContainInOrder(workflow03.ExecutionPointers.FindById("1").Scope); + } + [Fact] public void PersistWorkflow() { @@ -150,4 +233,4 @@ public class TestData { public int Value1 { get; set; } } -} +} \ No newline at end of file From 0ccb7928331296c0ce2aeecb5f995ef103966a63 Mon Sep 17 00:00:00 2001 From: Demian Marty Date: Tue, 12 Mar 2019 16:30:12 +0100 Subject: [PATCH 116/462] fix typo in UT --- test/WorkflowCore.UnitTests/BasePersistenceFixture.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs index f005aa48b..87b733a2d 100644 --- a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs +++ b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs @@ -110,7 +110,7 @@ public void GetWorkflowInstances_should_retrieve_workflows() SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime(), Scope = new List() { "4", "3", "2", "1" } }); - var workflowId02 = Subject.CreateNewWorkflow(workflow01).Result; + var workflowId02 = Subject.CreateNewWorkflow(workflow02).Result; var workflow03 = new WorkflowInstance() { @@ -130,7 +130,7 @@ public void GetWorkflowInstances_should_retrieve_workflows() SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime(), Scope = new List() { "4", "3", "2", "1" } }); - var workflowId03 = Subject.CreateNewWorkflow(workflow01).Result; + var workflowId03 = Subject.CreateNewWorkflow(workflow03).Result; var retrievedWorkflows = Subject.GetWorkflowInstances(new[] { workflowId01, workflowId02, workflowId03 }).Result; From 8f412082696c0eda8b3e280e8035a08ea90fecb3 Mon Sep 17 00:00:00 2001 From: Demian Marty Date: Thu, 14 Mar 2019 09:43:23 +0100 Subject: [PATCH 117/462] fix GetWorkflowInstances for DynamoDB provider --- .../Services/DynamoPersistenceProvider.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index 585fc226c..bbe61ae18 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -125,10 +125,16 @@ public async Task> GetWorkflowInstances(IEnumerabl return new List(); } - var keys = new Dictionary(); + var keys = new KeysAndAttributes() { Keys = new List>() }; foreach (var id in ids) { - keys.Add("id", new AttributeValue { S = id }); + var key = new Dictionary() + { + { + "id", new AttributeValue { S = id } + } + }; + keys.Keys.Add(key); } var request = new BatchGetItemRequest @@ -136,11 +142,7 @@ public async Task> GetWorkflowInstances(IEnumerabl RequestItems = new Dictionary() { { - $"{_tablePrefix}-{WORKFLOW_TABLE}", - new KeysAndAttributes - { - Keys = new List >() { keys } - } + $"{_tablePrefix}-{WORKFLOW_TABLE}", keys } } }; From ded764546bf1ea0cf1a816aee3ad99eec5b4e93d Mon Sep 17 00:00:00 2001 From: Demian Marty Date: Fri, 15 Mar 2019 17:24:27 +0100 Subject: [PATCH 118/462] fix typo --- src/WorkflowCore/Interface/IPersistenceProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WorkflowCore/Interface/IPersistenceProvider.cs b/src/WorkflowCore/Interface/IPersistenceProvider.cs index 58645b495..2c2d707ef 100644 --- a/src/WorkflowCore/Interface/IPersistenceProvider.cs +++ b/src/WorkflowCore/Interface/IPersistenceProvider.cs @@ -7,7 +7,7 @@ namespace WorkflowCore.Interface { /// /// The implemention of this interface will be responsible for - /// persisiting running workflow instances to a durable store + /// persisting running workflow instances to a durable store /// public interface IPersistenceProvider { From f0eb7163c8f01c55a4eddff5352a5d05528cc500 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Mar 2019 07:54:04 -0700 Subject: [PATCH 119/462] Issue 273 (#281) --- .../Services/CancellationProcessor.cs | 10 +- src/WorkflowCore/Services/WorkflowExecutor.cs | 191 +++++++++--------- 2 files changed, 106 insertions(+), 95 deletions(-) diff --git a/src/WorkflowCore/Services/CancellationProcessor.cs b/src/WorkflowCore/Services/CancellationProcessor.cs index d32d43f1f..e75d94f05 100644 --- a/src/WorkflowCore/Services/CancellationProcessor.cs +++ b/src/WorkflowCore/Services/CancellationProcessor.cs @@ -39,15 +39,15 @@ public void ProcessCancellations(WorkflowInstance workflow, WorkflowDefinition w foreach (var ptr in toCancel) { - ptr.EndTime = DateTime.Now.ToUniversalTime(); - ptr.Active = false; - ptr.Status = PointerStatus.Cancelled; - if (step.ProceedOnCancel) { _executionResultProcessor.ProcessExecutionResult(workflow, workflowDef, ptr, step, ExecutionResult.Next(), executionResult); } - + + ptr.EndTime = DateTime.Now.ToUniversalTime(); + ptr.Active = false; + ptr.Status = PointerStatus.Cancelled; + foreach (var descendent in workflow.ExecutionPointers.FindByScope(ptr.Id).Where(x => x.Status != PointerStatus.Complete && x.Status != PointerStatus.Cancelled)) { descendent.EndTime = DateTime.Now.ToUniversalTime(); diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 46f05d4f7..ce52e26b7 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -48,10 +48,12 @@ public async Task Execute(WorkflowInstance workflow) _logger.LogError("Workflow {0} version {1} is not registered", workflow.WorkflowDefinitionId, workflow.Version); return wfResult; } + + _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); foreach (var pointer in exePointers) { - if (pointer.Status == PointerStatus.Cancelled) + if (!pointer.Active) continue; var step = def.Steps.FindById(pointer.StepId); @@ -64,97 +66,17 @@ public async Task Execute(WorkflowInstance workflow) WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), - Message = String.Format("Unable to find step {0} in workflow definition", pointer.StepId) + Message = $"Unable to find step {pointer.StepId} in workflow definition" }); continue; } try - { - switch (step.InitForExecution(wfResult, def, workflow, pointer)) - { - case ExecutionPipelineDirective.Defer: - continue; - case ExecutionPipelineDirective.EndWorkflow: - workflow.Status = WorkflowStatus.Complete; - workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); - continue; - } - - if (pointer.Status != PointerStatus.Running) - { - pointer.Status = PointerStatus.Running; - _publisher.PublishNotification(new StepStarted() - { - EventTimeUtc = _datetimeProvider.Now, - Reference = workflow.Reference, - ExecutionPointerId = pointer.Id, - StepId = step.Id, - WorkflowInstanceId = workflow.Id, - WorkflowDefinitionId = workflow.WorkflowDefinitionId, - Version = workflow.Version - }); - } - - if (!pointer.StartTime.HasValue) - { - pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); - } + { + if (!InitializeStep(workflow, step, wfResult, def, pointer)) + continue; - using (var scope = _scopeProvider.CreateScope()) - { - _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); - - IStepBody body = step.ConstructBody(scope.ServiceProvider); - - if (body == null) - { - _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); - pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); - wfResult.Errors.Add(new ExecutionError() - { - WorkflowId = workflow.Id, - ExecutionPointerId = pointer.Id, - ErrorTime = _datetimeProvider.Now.ToUniversalTime(), - Message = String.Format("Unable to construct step body {0}", step.BodyType.ToString()) - }); - continue; - } - - IStepExecutionContext context = new StepExecutionContext() - { - Workflow = workflow, - Step = step, - PersistenceData = pointer.PersistenceData, - ExecutionPointer = pointer, - Item = pointer.ContextItem - }; - - foreach (var input in step.Inputs) - input.AssignInput(workflow.Data, body, context); - - - switch (step.BeforeExecute(wfResult, context, pointer, body)) - { - case ExecutionPipelineDirective.Defer: - continue; - case ExecutionPipelineDirective.EndWorkflow: - workflow.Status = WorkflowStatus.Complete; - workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); - continue; - } - - var result = await body.RunAsync(context); - - if (result.Proceed) - { - foreach (var output in step.Outputs) - output.AssignOutput(workflow.Data, body, context); - } - - _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); - step.AfterExecute(wfResult, context, result, pointer); - } + await ExecuteStep(workflow, step, pointer, wfResult, def); } catch (Exception ex) { @@ -171,16 +93,105 @@ public async Task Execute(WorkflowInstance workflow) Host.ReportStepError(workflow, step, ex); } _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); - - - } ProcessAfterExecutionIteration(workflow, def, wfResult); DetermineNextExecutionTime(workflow); return wfResult; } - + + private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, WorkflowExecutorResult wfResult, WorkflowDefinition def, ExecutionPointer pointer) + { + switch (step.InitForExecution(wfResult, def, workflow, pointer)) + { + case ExecutionPipelineDirective.Defer: + return false; + case ExecutionPipelineDirective.EndWorkflow: + workflow.Status = WorkflowStatus.Complete; + workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); + return false; + } + + if (pointer.Status != PointerStatus.Running) + { + pointer.Status = PointerStatus.Running; + _publisher.PublishNotification(new StepStarted() + { + EventTimeUtc = _datetimeProvider.Now, + Reference = workflow.Reference, + ExecutionPointerId = pointer.Id, + StepId = step.Id, + WorkflowInstanceId = workflow.Id, + WorkflowDefinitionId = workflow.WorkflowDefinitionId, + Version = workflow.Version + }); + } + + if (!pointer.StartTime.HasValue) + { + pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); + } + + return true; + } + + private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, ExecutionPointer pointer, WorkflowExecutorResult wfResult, WorkflowDefinition def) + { + using (var scope = _scopeProvider.CreateScope()) + { + _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); + + IStepBody body = step.ConstructBody(scope.ServiceProvider); + + if (body == null) + { + _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); + pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); + wfResult.Errors.Add(new ExecutionError() + { + WorkflowId = workflow.Id, + ExecutionPointerId = pointer.Id, + ErrorTime = _datetimeProvider.Now.ToUniversalTime(), + Message = $"Unable to construct step body {step.BodyType.ToString()}" + }); + return; + } + + IStepExecutionContext context = new StepExecutionContext() + { + Workflow = workflow, + Step = step, + PersistenceData = pointer.PersistenceData, + ExecutionPointer = pointer, + Item = pointer.ContextItem + }; + + foreach (var input in step.Inputs) + input.AssignInput(workflow.Data, body, context); + + switch (step.BeforeExecute(wfResult, context, pointer, body)) + { + case ExecutionPipelineDirective.Defer: + return; + case ExecutionPipelineDirective.EndWorkflow: + workflow.Status = WorkflowStatus.Complete; + workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); + return; + } + + var result = await body.RunAsync(context); + + if (result.Proceed) + { + foreach (var output in step.Outputs) + output.AssignOutput(workflow.Data, body, context); + } + + _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); + step.AfterExecute(wfResult, context, result, pointer); + } + } + private void ProcessAfterExecutionIteration(WorkflowInstance workflow, WorkflowDefinition workflowDef, WorkflowExecutorResult workflowResult) { var pointers = workflow.ExecutionPointers.Where(x => x.EndTime == null); From cf002b8bf2602d05e126a990712c3316bae13db4 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Mar 2019 17:48:13 -0700 Subject: [PATCH 120/462] bump versions --- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- .../WorkflowCore.Persistence.EntityFramework.csproj | 6 +++--- .../WorkflowCore.Persistence.MongoDB.csproj | 6 +++--- .../WorkflowCore.Persistence.MySQL.csproj | 6 +++--- .../WorkflowCore.Persistence.PostgreSQL.csproj | 6 +++--- .../WorkflowCore.Persistence.SqlServer.csproj | 6 +++--- .../WorkflowCore.Persistence.Sqlite.csproj | 6 +++--- .../Services/DynamoPersistenceProvider.cs | 12 ++---------- .../WorkflowCore.Providers.AWS.csproj | 4 ++-- .../WorkflowCore.Providers.Redis.csproj | 2 +- 10 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index ecbc04983..77f481752 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.9.0 - 1.9.0.0 - 1.9.0.0 + 1.9.1 + 1.9.1.0 + 1.9.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 321fe4915..b8ecefc08 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -15,10 +15,10 @@ false false false - 1.8.1 + 1.9.1 Base package for Workflow-core peristence providers using entity framework - 1.8.1.0 - 1.8.1.0 + 1.9.1.0 + 1.9.1.0 diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 4da3c1b7b..32ce70155 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -17,10 +17,10 @@ false false false - 1.7.0 + 1.9.0 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 1.7.0.0 - 1.7.0.0 + 1.9.0.0 + 1.9.0.0 diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index 989df4ceb..a7e705846 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a MySQL database. - 1.1.0 - 1.1.0.0 - 1.1.0.0 + 1.2.0 + 1.2.0.0 + 1.2.0.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 885d4611a..4524afe6f 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 1.8.1 - 1.8.1.0 - 1.8.1.0 + 1.9.1 + 1.9.1.0 + 1.9.1.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 3535ef304..432d8f1d4 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,10 +15,10 @@ false false false - 1.8.1 + 1.9.1 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 1.8.1.0 - 1.8.1.0 + 1.9.1.0 + 1.9.1.0 diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 128caa18f..dd4fce079 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 1.7.1 - 1.7.1.0 - 1.7.1.0 + 1.9.1 + 1.9.1.0 + 1.9.1.0 diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index bbe61ae18..9669d8346 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -151,19 +151,11 @@ public async Task> GetWorkflowInstances(IEnumerabl BatchGetItemResponse response; do { - // Making request response = await _client.BatchGetItemAsync(request); - - // Check the response - var responses = response.Responses; // Attribute list in the response. - foreach (var tableResponse in responses) - { + foreach (var tableResponse in response.Responses) result.AddRange(tableResponse.Value); - } - // Any unprocessed keys? could happen if you exceed ProvisionedThroughput or some other error. - Dictionary unprocessedKeys = response.UnprocessedKeys; - request.RequestItems = unprocessedKeys; + request.RequestItems = response.UnprocessedKeys; } while (response.UnprocessedKeys.Count > 0); return result.Select(i => i.ToWorkflowInstance()); diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 0eed166ea..3d44d7cd0 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,8 +11,8 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 1.9.0 - 1.9.0.0 + 1.9.1 + 1.9.1.0 diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index 4c5ec6550..994f2aebd 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.9.0 + 1.9.1 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git From a3e37f59b88943637fe5b84c4349174e8ee47364 Mon Sep 17 00:00:00 2001 From: Eddie Wood Date: Mon, 25 Mar 2019 09:27:19 -0600 Subject: [PATCH 121/462] WorkflowOptions support for configuring max number of concurrent tasks. --- src/WorkflowCore/Models/WorkflowOptions.cs | 13 +++++++++++++ .../Services/BackgroundTasks/EventConsumer.cs | 2 +- .../Services/BackgroundTasks/QueueConsumer.cs | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index dd8f0cec3..6f5f57916 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -8,6 +8,8 @@ namespace WorkflowCore.Models { public class WorkflowOptions { + internal static readonly int MinimumNumberOfConcurrentItems = 2; + internal Func PersistanceFactory; internal Func QueueFactory; internal Func LockFactory; @@ -16,6 +18,7 @@ public class WorkflowOptions internal TimeSpan PollInterval; internal TimeSpan IdleTime; internal TimeSpan ErrorRetryInterval; + internal int? MaxConcurrentItems; public IServiceCollection Services { get; private set; } @@ -67,6 +70,16 @@ public void UseErrorRetryInterval(TimeSpan interval) { ErrorRetryInterval = interval; } + + public void UseMaxConcurrentItems(int? maxConcurrentItems) + { + if (maxConcurrentItems.HasValue && maxConcurrentItems.Value < MinimumNumberOfConcurrentItems) + { + throw new ArgumentOutOfRangeException($"If {nameof(maxConcurrentItems)} is specified, it cannot be less than ${MinimumNumberOfConcurrentItems}."); + } + + MaxConcurrentItems = maxConcurrentItems; + } } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index ff49621e3..ab417619a 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -13,7 +13,7 @@ internal class EventConsumer : QueueConsumer, IBackgroundTask private readonly IPersistenceProvider _persistenceStore; private readonly IDistributedLockProvider _lockProvider; private readonly IDateTimeProvider _datetimeProvider; - protected override int MaxConcurrentItems => 2; + protected override int MaxConcurrentItems => WorkflowOptions.MinimumNumberOfConcurrentItems; protected override QueueType Queue => QueueType.Event; public EventConsumer(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, WorkflowOptions options, IDateTimeProvider datetimeProvider) diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 959ab24b1..5a1c3ab4c 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -11,7 +11,7 @@ namespace WorkflowCore.Services.BackgroundTasks internal abstract class QueueConsumer : IBackgroundTask { protected abstract QueueType Queue { get; } - protected virtual int MaxConcurrentItems => Math.Max(Environment.ProcessorCount, 2); + protected virtual int MaxConcurrentItems => Options.MaxConcurrentItems ?? Math.Max(Environment.ProcessorCount, WorkflowOptions.MinimumNumberOfConcurrentItems); protected virtual bool EnableSecondPasses => false; protected readonly IQueueProvider QueueProvider; From 3b74e77abe0e6666f8eab3de16c330c4ed87964c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 31 Mar 2019 06:53:57 -0700 Subject: [PATCH 122/462] Default retry behavior inside saga (#295) --- .../Services/ExecutionResultProcessor.cs | 12 ++-- .../Scenarios/DynamicDataIOScenario.cs | 2 +- .../Scenarios/FailingSagaScenario.cs | 65 +++++++++++++++++++ 3 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/FailingSagaScenario.cs diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index cdf0a9b42..730dbc911 100755 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -110,8 +110,8 @@ public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition de { var exceptionPointer = queue.Dequeue(); var exceptionStep = def.Steps.FindById(exceptionPointer.StepId); - var compensatingStepId = FindScopeCompensationStepId(workflow, def, exceptionPointer); - var errorOption = (exceptionStep.ErrorBehavior ?? (compensatingStepId.HasValue ? WorkflowErrorHandling.Compensate : def.DefaultErrorBehavior)); + var shouldCompensate = ShouldCompensate(workflow, def, exceptionPointer); + var errorOption = (exceptionStep.ErrorBehavior ?? (shouldCompensate ? WorkflowErrorHandling.Compensate : def.DefaultErrorBehavior)); foreach (var handler in _errorHandlers.Where(x => x.Type == errorOption)) { @@ -120,7 +120,7 @@ public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition de } } - private int? FindScopeCompensationStepId(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer currentPointer) + private bool ShouldCompensate(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer currentPointer) { var scope = new Stack(currentPointer.Scope); scope.Push(currentPointer.Id); @@ -130,11 +130,11 @@ public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition de var pointerId = scope.Pop(); var pointer = workflow.ExecutionPointers.FindById(pointerId); var step = def.Steps.FindById(pointer.StepId); - if (step.CompensationStepId.HasValue) - return step.CompensationStepId.Value; + if ((step.CompensationStepId.HasValue) || (step.RevertChildrenAfterCompensation)) + return true; } - return null; + return false; } } } \ No newline at end of file diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs index 4c1c013ba..416db55f2 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs @@ -38,7 +38,7 @@ public int this[string propertyName] public class DataIOWorkflow : IWorkflow { - public string Id => "DataIOWorkflow"; + public string Id => "DynamicDataIOWorkflow"; public int Version => 1; public void Build(IWorkflowBuilder builder) { diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/FailingSagaScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/FailingSagaScenario.cs new file mode 100644 index 000000000..36c12c3bd --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/FailingSagaScenario.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using System.Linq; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class FailingSagaScenario : WorkflowTest + { + public class Workflow : IWorkflow + { + public static int Event1Fired; + public static int Event2Fired; + public static int Event3Fired; + + public string Id => "NestedRetrySaga2Workflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .Saga(x => x + .StartWith(context => ExecutionResult.Next()) + .If(data => true) + .Do(i => i + .StartWith(context => + { + Event1Fired++; + throw new Exception(); + }) + ) + .Then(context => Event2Fired++) + ) + .OnError(WorkflowErrorHandling.Terminate) + .Then(context => Event3Fired++); + } + } + + public FailingSagaScenario() + { + Setup(); + Workflow.Event1Fired = 0; + Workflow.Event2Fired = 0; + Workflow.Event3Fired = 0; + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(null); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Terminated); + UnhandledStepErrors.Count.Should().Be(1); + Workflow.Event1Fired.Should().Be(1); + Workflow.Event2Fired.Should().Be(0); + Workflow.Event3Fired.Should().Be(0); + } + } +} From 90bed8e999ee55c0cf2f81a80728f47e1e8c9fe2 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 31 Mar 2019 06:56:37 -0700 Subject: [PATCH 123/462] bump versions --- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 77f481752..37510dff5 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.9.1 - 1.9.1.0 - 1.9.1.0 + 1.9.2 + 1.9.2.0 + 1.9.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From 49b46212a68853631425afc0817a0e41bd0aac5a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 31 Mar 2019 07:39:44 -0700 Subject: [PATCH 124/462] release notes --- ReleaseNotes/1.9.2.md | 4 ++++ WorkflowCore.sln | 1 + 2 files changed, 5 insertions(+) create mode 100644 ReleaseNotes/1.9.2.md diff --git a/ReleaseNotes/1.9.2.md b/ReleaseNotes/1.9.2.md new file mode 100644 index 000000000..fd84e938d --- /dev/null +++ b/ReleaseNotes/1.9.2.md @@ -0,0 +1,4 @@ +# Workflow Core 1.9.2 + +Changes the default retry behavior for steps within a saga to bubble up to the saga container. +This means you do not have to explicitly set each step within the saga to `Compensate`. \ No newline at end of file diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 02433997b..c7df64965 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -98,6 +98,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.8.0.md = ReleaseNotes\1.8.0.md ReleaseNotes\1.8.1.md = ReleaseNotes\1.8.1.md ReleaseNotes\1.9.0.md = ReleaseNotes\1.9.0.md + ReleaseNotes\1.9.2.md = ReleaseNotes\1.9.2.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" From cf253324b8cb2c6dd4dfa765ac7f0e4b82b77d34 Mon Sep 17 00:00:00 2001 From: Eddie Wood Date: Tue, 2 Apr 2019 10:59:13 -0600 Subject: [PATCH 125/462] Change MaxConcurrentItems to MaxConcurrentWorkflows. Make feature specific to WorkflowConsumer only. --- src/WorkflowCore/Models/WorkflowOptions.cs | 12 ++++++------ .../Services/BackgroundTasks/EventConsumer.cs | 2 +- .../Services/BackgroundTasks/QueueConsumer.cs | 2 +- .../Services/BackgroundTasks/WorkflowConsumer.cs | 2 ++ 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index 6f5f57916..f6727a107 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Models { public class WorkflowOptions { - internal static readonly int MinimumNumberOfConcurrentItems = 2; + internal static readonly int MinimumNumberOfConcurrentWorkflows = 2; internal Func PersistanceFactory; internal Func QueueFactory; @@ -18,7 +18,7 @@ public class WorkflowOptions internal TimeSpan PollInterval; internal TimeSpan IdleTime; internal TimeSpan ErrorRetryInterval; - internal int? MaxConcurrentItems; + internal int? MaxConcurrentWorkflows; public IServiceCollection Services { get; private set; } @@ -71,14 +71,14 @@ public void UseErrorRetryInterval(TimeSpan interval) ErrorRetryInterval = interval; } - public void UseMaxConcurrentItems(int? maxConcurrentItems) + public void UseMaxConcurrentWorkflows(int? maxConcurrentWorkflows) { - if (maxConcurrentItems.HasValue && maxConcurrentItems.Value < MinimumNumberOfConcurrentItems) + if (maxConcurrentWorkflows.HasValue && maxConcurrentWorkflows.Value < MinimumNumberOfConcurrentWorkflows) { - throw new ArgumentOutOfRangeException($"If {nameof(maxConcurrentItems)} is specified, it cannot be less than ${MinimumNumberOfConcurrentItems}."); + throw new ArgumentOutOfRangeException($"If {nameof(maxConcurrentWorkflows)} is specified, it cannot be less than ${MinimumNumberOfConcurrentWorkflows}."); } - MaxConcurrentItems = maxConcurrentItems; + MaxConcurrentWorkflows = maxConcurrentWorkflows; } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index ab417619a..ff49621e3 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -13,7 +13,7 @@ internal class EventConsumer : QueueConsumer, IBackgroundTask private readonly IPersistenceProvider _persistenceStore; private readonly IDistributedLockProvider _lockProvider; private readonly IDateTimeProvider _datetimeProvider; - protected override int MaxConcurrentItems => WorkflowOptions.MinimumNumberOfConcurrentItems; + protected override int MaxConcurrentItems => 2; protected override QueueType Queue => QueueType.Event; public EventConsumer(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, WorkflowOptions options, IDateTimeProvider datetimeProvider) diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 5a1c3ab4c..959ab24b1 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -11,7 +11,7 @@ namespace WorkflowCore.Services.BackgroundTasks internal abstract class QueueConsumer : IBackgroundTask { protected abstract QueueType Queue { get; } - protected virtual int MaxConcurrentItems => Options.MaxConcurrentItems ?? Math.Max(Environment.ProcessorCount, WorkflowOptions.MinimumNumberOfConcurrentItems); + protected virtual int MaxConcurrentItems => Math.Max(Environment.ProcessorCount, 2); protected virtual bool EnableSecondPasses => false; protected readonly IQueueProvider QueueProvider; diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 1ff0f67fe..93c0ecb41 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -15,6 +15,8 @@ internal class WorkflowConsumer : QueueConsumer, IBackgroundTask private readonly ObjectPool _persistenceStorePool; private readonly ObjectPool _executorPool; + protected override int MaxConcurrentItems => Options.MaxConcurrentWorkflows ?? base.MaxConcurrentItems; + protected override QueueType Queue => QueueType.Workflow; public WorkflowConsumer(IPooledObjectPolicy persistencePoolPolicy, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IPooledObjectPolicy executorPoolPolicy, IDateTimeProvider datetimeProvider, WorkflowOptions options) From b7584e50ec16ef98c0d85cdfdae2383216541d30 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 7 Apr 2019 07:45:07 -0700 Subject: [PATCH 126/462] Event order (#300) --- .../Services/BackgroundTasks/EventConsumer.cs | 35 ++++++--- .../Scenarios/EventOrderScenario.cs | 72 +++++++++++++++++++ 2 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/EventOrderScenario.cs diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index ff49621e3..49fc847cd 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -39,17 +40,17 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance if (evt.EventTime <= _datetimeProvider.Now.ToUniversalTime()) { var subs = await _persistenceStore.GetSubcriptions(evt.EventName, evt.EventKey, evt.EventTime); - var success = true; + var toQueue = new List(); + var complete = true; foreach (var sub in subs.ToList()) - { - success = success && await SeedSubscription(evt, sub, cancellationToken); - } + complete = complete && await SeedSubscription(evt, sub, toQueue, cancellationToken); - if (success) - { + if (complete) await _persistenceStore.MarkEventProcessed(itemId); - } + + foreach (var eventId in toQueue) + await QueueProvider.QueueWork(eventId, QueueType.Event); } } finally @@ -58,8 +59,24 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance } } - private async Task SeedSubscription(Event evt, EventSubscription sub, CancellationToken cancellationToken) - { + private async Task SeedSubscription(Event evt, EventSubscription sub, List toQueue, CancellationToken cancellationToken) + { + foreach (var eventId in await _persistenceStore.GetEvents(sub.EventName, sub.EventKey, sub.SubscribeAsOf)) + { + if (eventId == evt.Id) + continue; + + var siblingEvent = await _persistenceStore.GetEvent(eventId); + if ((!siblingEvent.IsProcessed) && (siblingEvent.EventTime < evt.EventTime)) + { + await QueueProvider.QueueWork(eventId, QueueType.Event); + return false; + } + + if (!siblingEvent.IsProcessed) + toQueue.Add(siblingEvent.Id); + } + if (!await _lockProvider.AcquireLock(sub.WorkflowId, cancellationToken)) { Logger.LogInformation("Workflow locked {0}", sub.WorkflowId); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/EventOrderScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/EventOrderScenario.cs new file mode 100644 index 000000000..5ac5766ed --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/EventOrderScenario.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using System.Linq; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class EventOrderScenario : WorkflowTest + { + public class MyDataClass + { + public int Value1 { get; set; } + public int Value2 { get; set; } + public int Value3 { get; set; } + public int Value4 { get; set; } + public int Value5 { get; set; } + } + + public class EventWorkflow : IWorkflow + { + public string Id => "EventOrder"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .WaitFor("OrderedEvent", data => string.Empty, data => new DateTime(2000, 1, 1, 0, 1, 0)) + .Output(data => data.Value1, step => step.EventData) + .WaitFor("OrderedEvent", data => string.Empty, data => new DateTime(2000, 1, 1, 0, 2, 0)) + .Output(data => data.Value2, step => step.EventData) + .WaitFor("OrderedEvent", data => string.Empty, data => new DateTime(2000, 1, 1, 0, 3, 0)) + .Output(data => data.Value3, step => step.EventData) + .WaitFor("OrderedEvent", data => string.Empty, data => new DateTime(2000, 1, 1, 0, 4, 0)) + .Output(data => data.Value4, step => step.EventData) + .WaitFor("OrderedEvent", data => string.Empty, data => new DateTime(2000, 1, 1, 0, 5, 0)) + .Output(data => data.Value5, step => step.EventData); + } + } + + public EventOrderScenario() + { + Setup(); + } + + [Fact] + public void Scenario() + { + Host.PublishEvent("OrderedEvent", string.Empty, 1, new DateTime(2000, 1, 1, 0, 1, 1)); + Host.PublishEvent("OrderedEvent", string.Empty, 2, new DateTime(2000, 1, 1, 0, 2, 1)); + Host.PublishEvent("OrderedEvent", string.Empty, 3, new DateTime(2000, 1, 1, 0, 3, 1)); + Host.PublishEvent("OrderedEvent", string.Empty, 4, new DateTime(2000, 1, 1, 0, 4, 1)); + Host.PublishEvent("OrderedEvent", string.Empty, 5, new DateTime(2000, 1, 1, 0, 5, 1)); + + var workflowId = StartWorkflow(new MyDataClass()); + + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + GetData(workflowId).Value1.Should().Be(1); + GetData(workflowId).Value2.Should().Be(2); + GetData(workflowId).Value3.Should().Be(3); + GetData(workflowId).Value4.Should().Be(4); + GetData(workflowId).Value5.Should().Be(5); + } + } +} From 34d14a5300daac6113e754b9aef8cbe402a2d6bc Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 7 Apr 2019 07:57:55 -0700 Subject: [PATCH 127/462] bump version --- src/WorkflowCore/Models/WorkflowOptions.cs | 11 ++--------- .../Services/BackgroundTasks/WorkflowConsumer.cs | 3 +-- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index f6727a107..635223af5 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -8,8 +8,6 @@ namespace WorkflowCore.Models { public class WorkflowOptions { - internal static readonly int MinimumNumberOfConcurrentWorkflows = 2; - internal Func PersistanceFactory; internal Func QueueFactory; internal Func LockFactory; @@ -18,7 +16,7 @@ public class WorkflowOptions internal TimeSpan PollInterval; internal TimeSpan IdleTime; internal TimeSpan ErrorRetryInterval; - internal int? MaxConcurrentWorkflows; + internal int MaxConcurrentWorkflows = Math.Max(Environment.ProcessorCount, 2); public IServiceCollection Services { get; private set; } @@ -71,13 +69,8 @@ public void UseErrorRetryInterval(TimeSpan interval) ErrorRetryInterval = interval; } - public void UseMaxConcurrentWorkflows(int? maxConcurrentWorkflows) + public void UseMaxConcurrentWorkflows(int maxConcurrentWorkflows) { - if (maxConcurrentWorkflows.HasValue && maxConcurrentWorkflows.Value < MinimumNumberOfConcurrentWorkflows) - { - throw new ArgumentOutOfRangeException($"If {nameof(maxConcurrentWorkflows)} is specified, it cannot be less than ${MinimumNumberOfConcurrentWorkflows}."); - } - MaxConcurrentWorkflows = maxConcurrentWorkflows; } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 93c0ecb41..de8e7ad4a 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -15,8 +15,7 @@ internal class WorkflowConsumer : QueueConsumer, IBackgroundTask private readonly ObjectPool _persistenceStorePool; private readonly ObjectPool _executorPool; - protected override int MaxConcurrentItems => Options.MaxConcurrentWorkflows ?? base.MaxConcurrentItems; - + protected override int MaxConcurrentItems => Options.MaxConcurrentWorkflows; protected override QueueType Queue => QueueType.Workflow; public WorkflowConsumer(IPooledObjectPolicy persistencePoolPolicy, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IPooledObjectPolicy executorPoolPolicy, IDateTimeProvider datetimeProvider, WorkflowOptions options) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 37510dff5..315e87ba2 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.9.2 - 1.9.2.0 - 1.9.2.0 + 1.9.3 + 1.9.3.0 + 1.9.3.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From 2b2a8a6344f2bf9db1e1e3865890382b1e7b5c78 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 7 Apr 2019 08:18:39 -0700 Subject: [PATCH 128/462] release notes --- ReleaseNotes/1.9.3.md | 4 ++++ WorkflowCore.sln | 1 + 2 files changed, 5 insertions(+) create mode 100644 ReleaseNotes/1.9.3.md diff --git a/ReleaseNotes/1.9.3.md b/ReleaseNotes/1.9.3.md new file mode 100644 index 000000000..585336a22 --- /dev/null +++ b/ReleaseNotes/1.9.3.md @@ -0,0 +1,4 @@ +# Workflow Core 1.9.3 + +* Fixes the order of processing for multiple events with same name/key +* Adds `UseMaxConcurrentWorkflows` to WorkflowOptions to allow overriding the max number of concurrent workflows for a given node \ No newline at end of file diff --git a/WorkflowCore.sln b/WorkflowCore.sln index c7df64965..261e79bde 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -99,6 +99,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.8.1.md = ReleaseNotes\1.8.1.md ReleaseNotes\1.9.0.md = ReleaseNotes\1.9.0.md ReleaseNotes\1.9.2.md = ReleaseNotes\1.9.2.md + ReleaseNotes\1.9.3.md = ReleaseNotes\1.9.3.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" From 89a3eeac3c9597f2a06d2b5a22fd222aba00870c Mon Sep 17 00:00:00 2001 From: SergerGood Date: Wed, 10 Apr 2019 16:10:41 +0300 Subject: [PATCH 129/462] async --- .../Services/MongoPersistenceProvider.cs | 10 ++-- .../Interfaces/ISqlCommandExecutor.cs | 7 +-- .../ISqlServerQueueProviderMigrator.cs | 5 +- .../Services/SqlCommandExecutor.cs | 18 ++---- .../Services/SqlServerQueueProvider.cs | 13 +++-- .../SqlServerQueueProviderMigrator.cs | 58 +++++++++---------- 6 files changed, 51 insertions(+), 60 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index e59c1230a..7235adf58 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -1,13 +1,11 @@ -using MongoDB.Bson; -using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.IdGenerators; -using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; using System.Threading.Tasks; +using MongoDB.Driver.Linq; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -127,7 +125,7 @@ public async Task> GetWorkflowInstances(IEnumerabl public async Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) { - IQueryable result = WorkflowInstances.AsQueryable(); + IMongoQueryable result = WorkflowInstances.AsQueryable(); if (status.HasValue) result = result.Where(x => x.Status == status.Value); @@ -141,7 +139,7 @@ public async Task> GetWorkflowInstances(WorkflowSt if (createdTo.HasValue) result = result.Where(x => x.CreateTime <= createdTo.Value); - return result.Skip(skip).Take(take).ToList(); + return await result.Skip(skip).Take(take).ToListAsync(); } public async Task CreateEventSubscription(EventSubscription subscription) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlCommandExecutor.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlCommandExecutor.cs index ea4d4aac0..68996f348 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlCommandExecutor.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlCommandExecutor.cs @@ -1,11 +1,10 @@ #region using using System; -using System.Collections.Generic; -using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Linq; +using System.Threading.Tasks; #endregion @@ -13,7 +12,7 @@ namespace WorkflowCore.QueueProviders.SqlServer.Interfaces { public interface ISqlCommandExecutor { - TResult ExecuteScalar(IDbConnection cn, IDbTransaction tx, string cmdtext, params DbParameter[] parameters); - int ExecuteCommand(IDbConnection cn, IDbTransaction tx, string cmdtext, params DbParameter[] parameters); + Task ExecuteScalarAsync(SqlConnection cn, SqlTransaction tx, string cmdtext, params DbParameter[] parameters); + Task ExecuteCommandAsync(SqlConnection cn, SqlTransaction tx, string cmdtext, params DbParameter[] parameters); } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs index bbe916147..50f1173e1 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; namespace WorkflowCore.QueueProviders.SqlServer.Interfaces { public interface ISqlServerQueueProviderMigrator { - void MigrateDb(); - void CreateDb(); + Task MigrateDbAsync(); + Task CreateDbAsync(); } } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs index efa82cff8..3ff92713d 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs @@ -6,6 +6,7 @@ using System.Data.Common; using System.Data.SqlClient; using System.Linq; +using System.Threading.Tasks; using WorkflowCore.QueueProviders.SqlServer.Interfaces; #endregion @@ -14,7 +15,7 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services { public class SqlCommandExecutor : ISqlCommandExecutor { - public TResult ExecuteScalar(IDbConnection cn, IDbTransaction tx, string cmdtext, params DbParameter[] parameters) + public async Task ExecuteScalarAsync(SqlConnection cn, SqlTransaction tx, string cmdtext, params DbParameter[] parameters) { using (var cmd = cn.CreateCommand()) { @@ -24,11 +25,11 @@ public TResult ExecuteScalar(IDbConnection cn, IDbTransaction tx, strin foreach (var param in parameters) cmd.Parameters.Add(param); - return (TResult)cmd.ExecuteScalar(); + return (TResult)await cmd.ExecuteScalarAsync(); } } - public int ExecuteCommand(IDbConnection cn, IDbTransaction tx, string cmdtext, params DbParameter[] parameters) + public async Task ExecuteCommandAsync(SqlConnection cn, SqlTransaction tx, string cmdtext, params DbParameter[] parameters) { using (var cmd = cn.CreateCommand()) { @@ -38,17 +39,8 @@ public int ExecuteCommand(IDbConnection cn, IDbTransaction tx, string cmdtext, p foreach (var param in parameters) cmd.Parameters.Add(param); - return cmd.ExecuteNonQuery(); + return await cmd.ExecuteNonQueryAsync(); } } - - private IDbCommand CreateCommand(IDbConnection cn, IDbTransaction tx, string cmdtext) - { - var cmd = cn.CreateCommand(); - cmd.Transaction = tx; - cmd.CommandText = cmdtext; - - return cmd; - } } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs index dbb43ba51..c07ed2b76 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProvider.cs @@ -61,8 +61,8 @@ private static string GetFromResource(string file) public async Task Start() { - if (_canCreateDb) _migrator.CreateDb(); - if (_canMigrateDb) _migrator.MigrateDb(); + if (_canCreateDb) await _migrator.CreateDbAsync(); + if (_canMigrateDb) await _migrator.MigrateDbAsync(); } public async Task Stop() @@ -92,10 +92,10 @@ public async Task QueueWork(string id, QueueType queue) SqlConnection cn = new SqlConnection(_connectionString); try { - cn.Open(); + await cn.OpenAsync(); var par = _config.GetByQueue(queue); - _sqlCommandExecutor.ExecuteCommand(cn, null, _queueWorkCommand, + await _sqlCommandExecutor.ExecuteCommandAsync(cn, null, _queueWorkCommand, new SqlParameter("@initiatorService", par.InitiatorService), new SqlParameter("@targetService", par.TargetService), new SqlParameter("@contractName", par.ContractName), @@ -121,10 +121,11 @@ public async Task DequeueWork(QueueType queue, CancellationToken cancell SqlConnection cn = new SqlConnection(_connectionString); try { - cn.Open(); + await cn.OpenAsync(cancellationToken); + var par = _config.GetByQueue(queue); var sql = _dequeueWorkCommand.Replace("{queueName}", par.QueueName); - var msg = _sqlCommandExecutor.ExecuteScalar(cn, null, sql); + var msg = await _sqlCommandExecutor.ExecuteScalarAsync(cn, null, sql); return msg is DBNull ? null : (string)msg; } diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index 46e91b79f..a28f894e6 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -4,7 +4,7 @@ using System.Data.SqlClient; using System.Linq; using System.Text.RegularExpressions; - +using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.QueueProviders.SqlServer.Interfaces; @@ -30,10 +30,10 @@ public SqlServerQueueProviderMigrator(string connectionString, IQueueConfigProvi #region Migrate - public void MigrateDb() + public async Task MigrateDbAsync() { var cn = new SqlConnection(_connectionString); - cn.Open(); + await cn.OpenAsync(); var tx = cn.BeginTransaction(); try { @@ -46,14 +46,14 @@ public void MigrateDb() foreach (var item in queueConfigurations) { - CreateMessageType(cn, tx, item.MsgType); + await CreateMessageType(cn, tx, item.MsgType); - CreateContract(cn, tx, item.ContractName, item.MsgType); + await CreateContract(cn, tx, item.ContractName, item.MsgType); - CreateQueue(cn, tx, item.QueueName); + await CreateQueue(cn, tx, item.QueueName); - CreateService(cn, tx, item.InitiatorService, item.QueueName, item.ContractName); - CreateService(cn, tx, item.TargetService, item.QueueName, item.ContractName); + await CreateService(cn, tx, item.InitiatorService, item.QueueName, item.ContractName); + await CreateService(cn, tx, item.TargetService, item.QueueName, item.ContractName); } tx.Commit(); @@ -69,53 +69,53 @@ public void MigrateDb() } } - private void CreateService(SqlConnection cn, SqlTransaction tx, string name, string queueName, string contractName) + private async Task CreateService(SqlConnection cn, SqlTransaction tx, string name, string queueName, string contractName) { var cmdtext = @"select name from sys.services where name=@name"; - var existing = _sqlCommandExecutor.ExecuteScalar(cn, tx, cmdtext, new SqlParameter("@name", name)); + var existing = await _sqlCommandExecutor.ExecuteScalarAsync(cn, tx, cmdtext, new SqlParameter("@name", name)); if (!string.IsNullOrEmpty(existing)) return; - _sqlCommandExecutor.ExecuteCommand(cn, tx, $"CREATE SERVICE [{name}] ON QUEUE [{queueName}]([{contractName}]);"); + await _sqlCommandExecutor.ExecuteCommandAsync(cn, tx, $"CREATE SERVICE [{name}] ON QUEUE [{queueName}]([{contractName}]);"); } - private void CreateQueue(SqlConnection cn, SqlTransaction tx, string queueName) + private async Task CreateQueue(SqlConnection cn, SqlTransaction tx, string queueName) { var cmdtext = @"select name from sys.service_queues where name=@name"; - var existing = _sqlCommandExecutor.ExecuteScalar(cn, tx, cmdtext, new SqlParameter("@name", queueName)); + var existing = await _sqlCommandExecutor.ExecuteScalarAsync(cn, tx, cmdtext, new SqlParameter("@name", queueName)); if (!string.IsNullOrEmpty(existing)) return; - _sqlCommandExecutor.ExecuteCommand(cn, tx, $"CREATE QUEUE [{queueName}];"); + await _sqlCommandExecutor.ExecuteCommandAsync(cn, tx, $"CREATE QUEUE [{queueName}];"); } - private void CreateContract(SqlConnection cn, SqlTransaction tx, string contractName, string messageName) + private async Task CreateContract(SqlConnection cn, SqlTransaction tx, string contractName, string messageName) { var cmdtext = @"select name from sys.service_contracts where name=@name"; - var existing = _sqlCommandExecutor.ExecuteScalar(cn, tx, cmdtext, new SqlParameter("@name", contractName)); + var existing = await _sqlCommandExecutor.ExecuteScalarAsync(cn, tx, cmdtext, new SqlParameter("@name", contractName)); if (!string.IsNullOrEmpty(existing)) return; - _sqlCommandExecutor.ExecuteCommand(cn, tx, $"CREATE CONTRACT [{contractName}] ( [{messageName}] SENT BY INITIATOR);"); + await _sqlCommandExecutor.ExecuteCommandAsync(cn, tx, $"CREATE CONTRACT [{contractName}] ( [{messageName}] SENT BY INITIATOR);"); } - private void CreateMessageType(SqlConnection cn, SqlTransaction tx, string message) + private async Task CreateMessageType(SqlConnection cn, SqlTransaction tx, string message) { var cmdtext = @"select name from sys.service_message_types where name=@name"; - var existing = _sqlCommandExecutor.ExecuteScalar(cn, tx, cmdtext, new SqlParameter("@name", message)); + var existing = await _sqlCommandExecutor.ExecuteScalarAsync(cn, tx, cmdtext, new SqlParameter("@name", message)); if (!string.IsNullOrEmpty(existing)) return; - _sqlCommandExecutor.ExecuteCommand(cn, tx, $"CREATE MESSAGE TYPE [{message}] VALIDATION = NONE;"); + await _sqlCommandExecutor.ExecuteCommandAsync(cn, tx, $"CREATE MESSAGE TYPE [{message}] VALIDATION = NONE;"); } #endregion - public void CreateDb() + public async Task CreateDbAsync() { var builder = new SqlConnectionStringBuilder(_connectionString); var masterBuilder = new SqlConnectionStringBuilder(_connectionString); @@ -125,20 +125,20 @@ public void CreateDb() bool dbPresente; var cn = new SqlConnection(masterCnStr); - cn.Open(); + await cn.OpenAsync(); try { var cmd = cn.CreateCommand(); cmd.CommandText = "select name from sys.databases where name = @dbname"; cmd.Parameters.AddWithValue("@dbname", builder.InitialCatalog); - var found = cmd.ExecuteScalar(); + var found = await cmd.ExecuteScalarAsync(); dbPresente = (found != null); if (!dbPresente) { var createCmd = cn.CreateCommand(); createCmd.CommandText = "create database [" + builder.InitialCatalog + "]"; - createCmd.ExecuteNonQuery(); + await createCmd.ExecuteNonQueryAsync(); } } finally @@ -146,15 +146,15 @@ public void CreateDb() cn.Close(); } - EnableBroker(masterCnStr, builder.InitialCatalog); + await EnableBroker(masterCnStr, builder.InitialCatalog); } - private void EnableBroker(string masterCn, string db) + private async Task EnableBroker(string masterCn, string db) { var cn = new SqlConnection(masterCn); - cn.Open(); + await cn.OpenAsync(); - var isBrokerEnabled = _sqlCommandExecutor.ExecuteScalar(cn, null, @"select is_broker_enabled from sys.databases where name = @name", new SqlParameter("@name", db)); + var isBrokerEnabled = await _sqlCommandExecutor.ExecuteScalarAsync(cn, null, @"select is_broker_enabled from sys.databases where name = @name", new SqlParameter("@name", db)); if (isBrokerEnabled) return; @@ -162,7 +162,7 @@ private void EnableBroker(string masterCn, string db) var tx = cn.BeginTransaction(); try { - _sqlCommandExecutor.ExecuteCommand(cn, tx, $"ALTER DATABASE [{db}] SET ENABLE_BROKER;"); + await _sqlCommandExecutor.ExecuteCommandAsync(cn, tx, $"ALTER DATABASE [{db}] SET ENABLE_BROKER;"); tx.Commit(); } catch From 490378d9e3b0c5f545c703c2bb570748c1b8214d Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Wed, 17 Apr 2019 10:11:20 +0700 Subject: [PATCH 130/462] fix(sample): Fix InvalidCastException in WebApiSample --- .../WebApiSample/Controllers/EventsController.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/samples/WebApiSample/WebApiSample/Controllers/EventsController.cs b/src/samples/WebApiSample/WebApiSample/Controllers/EventsController.cs index faa70a2e3..68486e735 100644 --- a/src/samples/WebApiSample/WebApiSample/Controllers/EventsController.cs +++ b/src/samples/WebApiSample/WebApiSample/Controllers/EventsController.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; +using WebApiSample.Workflows; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -22,9 +23,9 @@ public EventsController(IWorkflowController workflowService) } [HttpPost("{eventName}/{eventKey}")] - public async Task Post(string eventName, string eventKey, [FromBody]object eventData) + public async Task Post(string eventName, string eventKey, [FromBody]MyDataClass eventData) { - await _workflowService.PublishEvent(eventName, eventKey, eventData); + await _workflowService.PublishEvent(eventName, eventKey, eventData.Value1); return Ok(); } From ba365ad2b0113b1524562d24a7b12fbbeaa875dd Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Wed, 17 Apr 2019 10:16:04 +0700 Subject: [PATCH 131/462] chore(sample): Add Swagger for WebApiSample --- .../Properties/launchSettings.json | 6 +++--- .../WebApiSample/WebApiSample/Startup.cs | 18 ++++++++++++++++++ .../WebApiSample/WebApiSample.csproj | 3 ++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/samples/WebApiSample/WebApiSample/Properties/launchSettings.json b/src/samples/WebApiSample/WebApiSample/Properties/launchSettings.json index b5a3dd6fc..83c2f62d6 100644 --- a/src/samples/WebApiSample/WebApiSample/Properties/launchSettings.json +++ b/src/samples/WebApiSample/WebApiSample/Properties/launchSettings.json @@ -12,7 +12,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "api/values", + "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -20,7 +20,7 @@ "WebApiSample": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "api/values", + "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, @@ -29,7 +29,7 @@ "Docker": { "commandName": "Docker", "launchBrowser": true, - "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/api/values" + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger" } } } \ No newline at end of file diff --git a/src/samples/WebApiSample/WebApiSample/Startup.cs b/src/samples/WebApiSample/WebApiSample/Startup.cs index d59257122..a58582b7d 100644 --- a/src/samples/WebApiSample/WebApiSample/Startup.cs +++ b/src/samples/WebApiSample/WebApiSample/Startup.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Nest; +using Swashbuckle.AspNetCore.Swagger; using WebApiSample.Workflows; using WorkflowCore.Interface; @@ -27,6 +28,13 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); + + // Register the Swagger generator, defining 1 or more Swagger documents + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" }); + }); + services.AddWorkflow(cfg => { cfg.UseMongoDB(@"mongodb://mongo:27017", "workflow"); @@ -41,6 +49,16 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) app.UseDeveloperExceptionPage(); } + // Enable middleware to serve generated Swagger as a JSON endpoint. + app.UseSwagger(); + + // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), + // specifying the Swagger JSON endpoint. + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); + }); + app.UseMvc(); var host = app.ApplicationServices.GetService(); diff --git a/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj b/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj index 938a7108e..e5545be0c 100644 --- a/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj +++ b/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 @@ -16,6 +16,7 @@ + From cd38f57456b4bdf9dacc1d3c60ad1cb8960696c5 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 12 May 2019 07:15:46 -0700 Subject: [PATCH 132/462] samples --- .../WebApiSample/WebApiSample/Startup.cs | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/samples/WebApiSample/WebApiSample/Startup.cs b/src/samples/WebApiSample/WebApiSample/Startup.cs index a58582b7d..13a19ddd8 100644 --- a/src/samples/WebApiSample/WebApiSample/Startup.cs +++ b/src/samples/WebApiSample/WebApiSample/Startup.cs @@ -28,18 +28,14 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); - - // Register the Swagger generator, defining 1 or more Swagger documents - services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" }); - }); - + services.AddWorkflow(cfg => { cfg.UseMongoDB(@"mongodb://mongo:27017", "workflow"); cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://elastic:9200")), "workflows"); }); + + services.AddSwaggerGen(c => c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" })); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) @@ -48,16 +44,9 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseDeveloperExceptionPage(); } - - // Enable middleware to serve generated Swagger as a JSON endpoint. - app.UseSwagger(); - - // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), - // specifying the Swagger JSON endpoint. - app.UseSwaggerUI(c => - { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); - }); + + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1")); app.UseMvc(); From d161f8402551184721b815f3de57c9594a1a267c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 6 Jun 2019 16:16:56 -0700 Subject: [PATCH 133/462] wip --- src/WorkflowCore/Models/ActionParameter.cs | 14 +++++++++++--- src/WorkflowCore/Primitives/WaitFor.cs | 1 - src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/WorkflowCore/Models/ActionParameter.cs b/src/WorkflowCore/Models/ActionParameter.cs index e08edca13..f6a3e4723 100644 --- a/src/WorkflowCore/Models/ActionParameter.cs +++ b/src/WorkflowCore/Models/ActionParameter.cs @@ -8,16 +8,24 @@ namespace WorkflowCore.Models { public class ActionParameter : IStepParameter { - private readonly Action _action; + private readonly Action _action; - public ActionParameter(Action action) + public ActionParameter(Action action) { _action = action; } + public ActionParameter(Action action) + { + _action = new Action((body, data, context) => + { + action(body, data); + }); + } + private void Assign(object data, IStepBody step, IStepExecutionContext context) { - _action.Invoke((TStepBody)step, (TData)data); + _action.Invoke((TStepBody)step, (TData)data, context); } public void AssignInput(object data, IStepBody body, IStepExecutionContext context) diff --git a/src/WorkflowCore/Primitives/WaitFor.cs b/src/WorkflowCore/Primitives/WaitFor.cs index e2a8a673f..7f84be2cd 100644 --- a/src/WorkflowCore/Primitives/WaitFor.cs +++ b/src/WorkflowCore/Primitives/WaitFor.cs @@ -20,7 +20,6 @@ public override ExecutionResult Run(IStepExecutionContext context) { DateTime effectiveDate = DateTime.MinValue; - // TODO: This will always execute. if (EffectiveDate != null) { effectiveDate = EffectiveDate; diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 315e87ba2..842d03ea2 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.9.3 - 1.9.3.0 - 1.9.3.0 + 1.9.4 + 1.9.4.0 + 1.9.4.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From c182dce2edadab28323651c184130df00e5b446d Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 30 Jun 2019 06:54:53 -0700 Subject: [PATCH 134/462] Target netstandard2, Yaml support, object graph support (#328) --- README.md | 15 ++- ReleaseNotes/2.0.0.md | 72 ++++++++++++ WorkflowCore.sln | 15 +-- .../Interface/IDefinitionLoader.cs | 6 +- .../DefinitionStorage/v1/StepSourceV1.cs | 3 +- .../DefinitionStorage/DefinitionLoader.cs | 76 ++++++++++-- .../DefinitionStorage/Deserializers.cs | 16 +++ src/WorkflowCore/WorkflowCore.csproj | 23 ++-- .../WorkflowCore.Users.csproj | 13 +-- .../WorkflowCore.WebAPI.csproj | 9 +- .../Properties/AssemblyInfo.cs | 19 --- .../README.md | 21 ---- .../ServiceCollectionExtensions.cs | 20 ---- .../Services/RedlockProvider.cs | 84 -------------- .../WorkflowCore.LockProviders.Redlock.csproj | 41 ------- ...orkflowCore.LockProviders.SqlServer.csproj | 6 +- ...lowCore.Persistence.EntityFramework.csproj | 7 +- .../WorkflowCore.Persistence.MongoDB.csproj | 13 +-- ...WorkflowCore.Persistence.PostgreSQL.csproj | 7 +- .../WorkflowCore.Persistence.SqlServer.csproj | 6 +- .../WorkflowCore.Providers.AWS.csproj | 6 +- .../WorkflowCore.Providers.Azure.csproj | 8 +- ...orkflowCore.Providers.Elasticsearch.csproj | 4 +- .../WorkflowCore.Providers.Redis.csproj | 4 +- ...orkflowCore.QueueProviders.RabbitMQ.csproj | 10 +- ...rkflowCore.QueueProviders.SqlServer.csproj | 4 +- .../WorkflowCore.Sample04.csproj | 2 +- .../WorkflowCore.Sample07.csproj | 2 +- test/ScratchPad/ScratchPad.csproj | 2 +- ...toredScenario.cs => StoredJsonScenario.cs} | 4 +- .../Scenarios/StoredYamlScenario.cs | 37 ++++++ .../Properties/Resources.Designer.cs | 32 ++--- .../Properties/Resources.resx | 5 +- test/WorkflowCore.TestAssets/Utils.cs | 6 +- .../WorkflowCore.TestAssets.csproj | 10 +- .../stored-definition.yaml | 58 ++++++++++ test/WorkflowCore.Testing/JsonWorkflowTest.cs | 3 +- test/WorkflowCore.Testing/YamlWorkflowTest.cs | 109 ++++++++++++++++++ .../WorkflowCore.Tests.MongoDB.csproj | 2 +- .../DefinitionLoaderTests.cs | 6 +- 40 files changed, 475 insertions(+), 311 deletions(-) create mode 100644 ReleaseNotes/2.0.0.md create mode 100644 src/WorkflowCore/Services/DefinitionStorage/Deserializers.cs delete mode 100644 src/providers/WorkflowCore.LockProviders.Redlock/Properties/AssemblyInfo.cs delete mode 100644 src/providers/WorkflowCore.LockProviders.Redlock/README.md delete mode 100644 src/providers/WorkflowCore.LockProviders.Redlock/ServiceCollectionExtensions.cs delete mode 100644 src/providers/WorkflowCore.LockProviders.Redlock/Services/RedlockProvider.cs delete mode 100644 src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj rename test/WorkflowCore.IntegrationTests/Scenarios/{StoredScenario.cs => StoredJsonScenario.cs} (96%) create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs create mode 100644 test/WorkflowCore.TestAssets/stored-definition.yaml create mode 100644 test/WorkflowCore.Testing/YamlWorkflowTest.cs diff --git a/README.md b/README.md index 07c05789b..c295eeee6 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,9 @@ public class MyWorkflow : IWorkflow } ``` -## JSON Workflow Definitions +## JSON / YAML Workflow Definitions -Define your workflows in JSON +Define your workflows in JSON or YAML ```json { @@ -47,6 +47,17 @@ Define your workflows in JSON } ``` +```yaml +Id: HelloWorld +Version: 1 +Steps: +- Id: Hello + StepType: MyApp.HelloWorld, MyApp + NextStepId: Bye +- Id: Bye + StepType: MyApp.GoodbyeWorld, MyApp +``` + ### Sample use cases * New user workflow diff --git a/ReleaseNotes/2.0.0.md b/ReleaseNotes/2.0.0.md new file mode 100644 index 000000000..c4021f4e8 --- /dev/null +++ b/ReleaseNotes/2.0.0.md @@ -0,0 +1,72 @@ +# Workflow Core 2.0.0 + +### Upgrade notes +Existing JSON definitions will be loaded as follows + ```c# + using WorkflowCore.Services.DefinitionStorage; + ... + DefinitionLoader.LoadDefinition(json, Deserializers.Json); + ``` + + +* Targets .NET Standard 2.0 + + The core library now targets .NET Standard 2.0, in order to leverage newer features. + +* Support for YAML definitions + + Added support for YAML workflow definitions, which can be loaded as follows + ```c# + using WorkflowCore.Services.DefinitionStorage; + ... + DefinitionLoader.LoadDefinition(json, Deserializers.Yaml); + ``` + + Existing JSON definitions will be loaded as follows + ```c# + using WorkflowCore.Services.DefinitionStorage; + ... + DefinitionLoader.LoadDefinition(json, Deserializers.Json); + ``` + +* Object graphs and inline expressions on input properties + + You can now pass object graphs to step inputs as opposed to just scalar values + ``` + "inputs": + { + "Body": { + "Value1": 1, + "Value2": 2 + }, + "Headers": { + "Content-Type": "application/json" + } + }, + ``` + If you want to evaluate an expression for a given property of your object, simply prepend and `@` and pass an expression string + ``` + "inputs": + { + "Body": { + "@Value1": "data.MyValue * 2", + "Value2": 5 + }, + "Headers": { + "Content-Type": "application/json" + } + }, + ``` + +* Support for enum values on input properties + + If your step has an enum property, you can now just pass the string representation of the enum value and it will be automatically converted. + +* Environment variables available in input expressions + + You can now access environment variables from within input expressions. + usage: + ``` + environment["VARIABLE_NAME"] + ``` + diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 261e79bde..2caad68c2 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -40,8 +40,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Persistence.Po EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Persistence.Sqlite", "src\providers\WorkflowCore.Persistence.Sqlite\WorkflowCore.Persistence.Sqlite.csproj", "{86BC1E05-E9CE-4E53-B324-885A2FDBCE74}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.LockProviders.Redlock", "src\providers\WorkflowCore.LockProviders.Redlock\WorkflowCore.LockProviders.Redlock.csproj", "{05250D58-A59E-4212-8D55-E7BC0396E9F5}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.QueueProviders.RabbitMQ", "src\providers\WorkflowCore.QueueProviders.RabbitMQ\WorkflowCore.QueueProviders.RabbitMQ.csproj", "{AFAD87C7-B2EE-451E-BA7E-3F5A91358C48}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample06", "src\samples\WorkflowCore.Sample06\WorkflowCore.Sample06.csproj", "{8FEAFD74-C304-4F75-BA38-4686BE55C891}" @@ -100,6 +98,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.9.0.md = ReleaseNotes\1.9.0.md ReleaseNotes\1.9.2.md = ReleaseNotes\1.9.2.md ReleaseNotes\1.9.3.md = ReleaseNotes\1.9.3.md + ReleaseNotes\2.0.0.md = ReleaseNotes\2.0.0.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" @@ -114,8 +113,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample15", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample16", "src\samples\WorkflowCore.Sample16\WorkflowCore.Sample16.csproj", "{0C9617A9-C8B7-45F6-A54A-261A23AC881B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchPad\ScratchPad.csproj", "{6396453F-4D0E-4CD4-BC89-87E8970F2A80}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample17", "src\samples\WorkflowCore.Sample17\WorkflowCore.Sample17.csproj", "{42F475BC-95F4-42E1-8CCD-7B9C27487E33}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.QueueProviders.SqlServer", "src\providers\WorkflowCore.QueueProviders.SqlServer\WorkflowCore.QueueProviders.SqlServer.csproj", "{7EDD9353-F5C2-414C-AE51-4B0F1C5E105A}" @@ -186,10 +183,6 @@ Global {86BC1E05-E9CE-4E53-B324-885A2FDBCE74}.Debug|Any CPU.Build.0 = Debug|Any CPU {86BC1E05-E9CE-4E53-B324-885A2FDBCE74}.Release|Any CPU.ActiveCfg = Release|Any CPU {86BC1E05-E9CE-4E53-B324-885A2FDBCE74}.Release|Any CPU.Build.0 = Release|Any CPU - {05250D58-A59E-4212-8D55-E7BC0396E9F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {05250D58-A59E-4212-8D55-E7BC0396E9F5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {05250D58-A59E-4212-8D55-E7BC0396E9F5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {05250D58-A59E-4212-8D55-E7BC0396E9F5}.Release|Any CPU.Build.0 = Release|Any CPU {AFAD87C7-B2EE-451E-BA7E-3F5A91358C48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AFAD87C7-B2EE-451E-BA7E-3F5A91358C48}.Debug|Any CPU.Build.0 = Debug|Any CPU {AFAD87C7-B2EE-451E-BA7E-3F5A91358C48}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -294,10 +287,6 @@ Global {0C9617A9-C8B7-45F6-A54A-261A23AC881B}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C9617A9-C8B7-45F6-A54A-261A23AC881B}.Release|Any CPU.ActiveCfg = Release|Any CPU {0C9617A9-C8B7-45F6-A54A-261A23AC881B}.Release|Any CPU.Build.0 = Release|Any CPU - {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6396453F-4D0E-4CD4-BC89-87E8970F2A80}.Release|Any CPU.Build.0 = Release|Any CPU {42F475BC-95F4-42E1-8CCD-7B9C27487E33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {42F475BC-95F4-42E1-8CCD-7B9C27487E33}.Debug|Any CPU.Build.0 = Debug|Any CPU {42F475BC-95F4-42E1-8CCD-7B9C27487E33}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -357,7 +346,6 @@ Global {1DE96D4F-F2CA-4740-8764-BADD1000040A} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {9274B938-3996-4FBA-AE2F-0C82009B1116} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {86BC1E05-E9CE-4E53-B324-885A2FDBCE74} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} - {05250D58-A59E-4212-8D55-E7BC0396E9F5} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {AFAD87C7-B2EE-451E-BA7E-3F5A91358C48} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {8FEAFD74-C304-4F75-BA38-4686BE55C891} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {37B598A8-B054-4ABA-884D-96AEF2511600} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} @@ -384,7 +372,6 @@ Global {EC497168-5347-4E70-9D9E-9C2F826C1CDF} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {9B7811AC-68D6-4D19-B1E9-65423393ED83} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {0C9617A9-C8B7-45F6-A54A-261A23AC881B} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} - {6396453F-4D0E-4CD4-BC89-87E8970F2A80} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {42F475BC-95F4-42E1-8CCD-7B9C27487E33} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {7EDD9353-F5C2-414C-AE51-4B0F1C5E105A} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {5E82A137-0954-46A1-8C46-13C00F0E4842} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} diff --git a/src/WorkflowCore/Interface/IDefinitionLoader.cs b/src/WorkflowCore/Interface/IDefinitionLoader.cs index cd1cf5fbb..c8870e679 100644 --- a/src/WorkflowCore/Interface/IDefinitionLoader.cs +++ b/src/WorkflowCore/Interface/IDefinitionLoader.cs @@ -1,9 +1,11 @@ -using WorkflowCore.Models; +using System; +using WorkflowCore.Models; +using WorkflowCore.Models.DefinitionStorage.v1; namespace WorkflowCore.Interface { public interface IDefinitionLoader { - WorkflowDefinition LoadDefinition(string json); + WorkflowDefinition LoadDefinition(string source, Func deserializer); } } \ No newline at end of file diff --git a/src/WorkflowCore/Models/DefinitionStorage/v1/StepSourceV1.cs b/src/WorkflowCore/Models/DefinitionStorage/v1/StepSourceV1.cs index fe351fa0f..350da2407 100644 --- a/src/WorkflowCore/Models/DefinitionStorage/v1/StepSourceV1.cs +++ b/src/WorkflowCore/Models/DefinitionStorage/v1/StepSourceV1.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Dynamic; using System.Text; namespace WorkflowCore.Models.DefinitionStorage.v1 @@ -26,7 +27,7 @@ public class StepSourceV1 public string NextStepId { get; set; } - public Dictionary Inputs { get; set; } = new Dictionary(); + public ExpandoObject Inputs { get; set; } = new ExpandoObject(); public Dictionary Outputs { get; set; } = new Dictionary(); diff --git a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs index fd44f7873..9d37dd9da 100644 --- a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs +++ b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs @@ -1,11 +1,13 @@ using Newtonsoft.Json; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; using System.Linq.Expressions; using System.Reflection; using System.Text; +using Newtonsoft.Json.Linq; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Primitives; @@ -24,10 +26,10 @@ public DefinitionLoader(IWorkflowRegistry registry) _registry = registry; } - public WorkflowDefinition LoadDefinition(string json) + public WorkflowDefinition LoadDefinition(string source, Func deserializer) { - var source = JsonConvert.DeserializeObject(json); - var def = Convert(source); + var sourceObj = deserializer(source); + var def = Convert(sourceObj); _registry.RegisterWorkflow(def); return def; } @@ -171,13 +173,24 @@ private void AttachInputs(StepSourceV1 source, Type dataType, Type stepType, Wor { var dataParameter = Expression.Parameter(dataType, "data"); var contextParameter = Expression.Parameter(typeof(IStepExecutionContext), "context"); - var sourceExpr = DynamicExpressionParser.ParseLambda(new [] { dataParameter, contextParameter }, typeof(object), input.Value); + var environmentVarsParameter = Expression.Parameter(typeof(IDictionary), "environment"); + var stepProperty = stepType.GetProperty(input.Key); - var stepParameter = Expression.Parameter(stepType, "step"); - var targetProperty = Expression.Property(stepParameter, input.Key); - var targetExpr = Expression.Lambda(targetProperty, stepParameter); + if (input.Value is string) + { + var acn = BuildScalarInputAction(input, dataParameter, contextParameter, environmentVarsParameter, stepProperty); + step.Inputs.Add(new ActionParameter(acn)); + continue; + } - step.Inputs.Add(new MemberMapParameter(sourceExpr, targetExpr)); + if ((input.Value is IDictionary) || (input.Value is IDictionary)) + { + var acn = BuildObjectInputAction(input, dataParameter, contextParameter, environmentVarsParameter, stepProperty); + step.Inputs.Add(new ActionParameter(acn)); + continue; + } + + throw new ArgumentException($"Unknown type for input {input.Key} on {source.Id}"); } } @@ -221,5 +234,52 @@ private Type FindType(string name) return Type.GetType(name, true, true); } + private static Action BuildScalarInputAction(KeyValuePair input, ParameterExpression dataParameter, ParameterExpression contextParameter, ParameterExpression environmentVarsParameter, PropertyInfo stepProperty) + { + var expr = System.Convert.ToString(input.Value); + var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { dataParameter, contextParameter, environmentVarsParameter }, typeof(object), expr); + + void acn(IStepBody pStep, object pData, IStepExecutionContext pContext) + { + object resolvedValue = sourceExpr.Compile().DynamicInvoke(pData, pContext, Environment.GetEnvironmentVariables()); + if (stepProperty.PropertyType.IsEnum) + stepProperty.SetValue(pStep, Enum.Parse(stepProperty.PropertyType, (string)resolvedValue, true)); + else + stepProperty.SetValue(pStep, System.Convert.ChangeType(resolvedValue, stepProperty.PropertyType)); + } + return acn; + } + + private static Action BuildObjectInputAction(KeyValuePair input, ParameterExpression dataParameter, ParameterExpression contextParameter, ParameterExpression environmentVarsParameter, PropertyInfo stepProperty) + { + void acn(IStepBody pStep, object pData, IStepExecutionContext pContext) + { + var stack = new Stack(); + var destObj = JObject.FromObject(input.Value); + stack.Push(destObj); + + while (stack.Count > 0) + { + var subobj = stack.Pop(); + foreach (var prop in subobj.Properties().ToList()) + { + if (prop.Name.StartsWith("@")) + { + var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { dataParameter, contextParameter, environmentVarsParameter }, typeof(object), prop.Value.ToString()); + object resolvedValue = sourceExpr.Compile().DynamicInvoke(pData, pContext, Environment.GetEnvironmentVariables()); + subobj.Remove(prop.Name); + subobj.Add(prop.Name.TrimStart('@'), JToken.FromObject(resolvedValue)); + } + } + + foreach (var child in subobj.Children()) + stack.Push(child); + } + + stepProperty.SetValue(pStep, destObj); + } + return acn; + } + } } diff --git a/src/WorkflowCore/Services/DefinitionStorage/Deserializers.cs b/src/WorkflowCore/Services/DefinitionStorage/Deserializers.cs new file mode 100644 index 000000000..4958f6f09 --- /dev/null +++ b/src/WorkflowCore/Services/DefinitionStorage/Deserializers.cs @@ -0,0 +1,16 @@ +using System; +using Newtonsoft.Json; +using SharpYaml.Serialization; +using WorkflowCore.Models.DefinitionStorage.v1; + +namespace WorkflowCore.Services.DefinitionStorage +{ + public static class Deserializers + { + private static Serializer yamlSerializer = new Serializer(); + + public static Func Json = (source) => JsonConvert.DeserializeObject(source); + + public static Func Yaml = (source) => yamlSerializer.DeserializeInto(source, new DefinitionSourceV1()); + } +} diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 842d03ea2..c9f2258b4 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -3,7 +3,7 @@ Workflow Core Daniel Gerlag - netstandard1.3 + netstandard2.0 WorkflowCore WorkflowCore workflow;.NET;Core;state machine @@ -15,33 +15,26 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 1.9.4 - 1.9.4.0 - 1.9.4.0 + 2.0.0 + 2.0.0.0 + 2.0.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - - - + + + + - - - - - - - - diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 9bb4c502f..2725b4c61 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -2,9 +2,8 @@ Workflow Core extensions for human workflow - 1.6.2 Daniel Gerlag - netstandard1.3 + netstandard2.0 WorkflowCore.Users WorkflowCore.Users workflow;.NET;Core;state machine;WorkflowCore;human;user @@ -12,15 +11,13 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 1.6.1 - $(PackageTargetFallback);dnxcore50 false false false Provides extensions for Workflow Core to enable human workflows. - 1.8.2 - 1.8.2.0 - 1.8.2.0 + 2.0.0 + 2.0.0.0 + 2.0.0.0 @@ -28,7 +25,7 @@ - + diff --git a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj index 74b5f8181..f12a9630f 100644 --- a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj +++ b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj @@ -2,9 +2,8 @@ Workflow Core REST API - 1.6.0 Daniel Gerlag - netstandard1.6 + netstandard2.0 WorkflowCore.WebAPI WorkflowCore.WebAPI workflow;.NET;Core;state machine;WorkflowCore;REST;API @@ -12,12 +11,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 1.6.1 - $(PackageTargetFallback);dnxcore50 false false false - 1.7.0 + 2.0.0 WebAPI wrapper for Workflow host @@ -28,7 +25,7 @@ - + diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/Properties/AssemblyInfo.cs b/src/providers/WorkflowCore.LockProviders.Redlock/Properties/AssemblyInfo.cs deleted file mode 100644 index 4c574c709..000000000 --- a/src/providers/WorkflowCore.LockProviders.Redlock/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("WorkflowCore.LockProviders.Redlock")] -[assembly: AssemblyTrademark("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("05250d58-a59e-4212-8d55-e7bc0396e9f5")] diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/README.md b/src/providers/WorkflowCore.LockProviders.Redlock/README.md deleted file mode 100644 index 866fa762b..000000000 --- a/src/providers/WorkflowCore.LockProviders.Redlock/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Redis Relock DLM provider for Workflow Core - -Provides [DLM](https://en.wikipedia.org/wiki/Distributed_lock_manager) support on [Workflow Core](../../README.md) using [Redis Redlock](http://redis.io/topics/distlock). - -This makes it possible to have a cluster of nodes processing your workflows, along with a queue provider. - -## Installing - -Install the NuGet package "WorkflowCore.LockProviders.Redlock" - -``` -PM> Install-Package WorkflowCore.LockProviders.Redlock -``` - -## Usage - -Use the .UseRedlock extension method when building your service provider. - -```C# -services.AddWorkflow(x => x.UseRedlock(new DnsEndPoint("host1", 6379), new DnsEndPoint("host2", 6379))); -``` \ No newline at end of file diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.LockProviders.Redlock/ServiceCollectionExtensions.cs deleted file mode 100644 index 2af4f0597..000000000 --- a/src/providers/WorkflowCore.LockProviders.Redlock/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using WorkflowCore.Models; -using WorkflowCore.LockProviders.Redlock.Services; -using StackExchange.Redis; -using System.Net; - -namespace Microsoft.Extensions.DependencyInjection -{ - public static class ServiceCollectionExtensions - { - public static WorkflowOptions UseRedlock(this WorkflowOptions options, params DnsEndPoint[] endpoints) - { - options.UseDistributedLockManager(sp => new RedlockProvider(endpoints)); - return options; - } - } -} diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/Services/RedlockProvider.cs b/src/providers/WorkflowCore.LockProviders.Redlock/Services/RedlockProvider.cs deleted file mode 100644 index 51273ac3b..000000000 --- a/src/providers/WorkflowCore.LockProviders.Redlock/Services/RedlockProvider.cs +++ /dev/null @@ -1,84 +0,0 @@ -using RedLockNet.SERedis; -using RedLockNet.SERedis.Configuration; -using RedLockNet; -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using WorkflowCore.Interface; - -namespace WorkflowCore.LockProviders.Redlock.Services -{ - public class RedlockProvider : IDistributedLockProvider, IDisposable - { - private readonly RedLockFactory _redlockFactory; - private readonly TimeSpan _lockTimeout = TimeSpan.FromMinutes(10); - private readonly List ManagedLocks = new List(); - - public RedlockProvider(params DnsEndPoint[] endpoints) - { - var redlockEndpoints = new List(); - - foreach (var ep in endpoints) - redlockEndpoints.Add(ep); - - - _redlockFactory = RedLockFactory.Create(redlockEndpoints); - - } - - public async Task AcquireLock(string Id, CancellationToken cancellationToken) - { - - var redLock = await _redlockFactory.CreateLockAsync(Id, _lockTimeout); - - if (redLock.IsAcquired) - { - lock (ManagedLocks) - { - ManagedLocks.Add(redLock); - } - return true; - } - - return false; - } - - - - public Task ReleaseLock(string Id) - { - lock (ManagedLocks) - { - foreach (var redLock in ManagedLocks) - { - if (redLock.Resource == Id) - { - redLock.Dispose(); - ManagedLocks.Remove(redLock); - break; - } - } - } - - return Task.CompletedTask; - } - - public Task Start() - { - return Task.CompletedTask; - } - - public Task Stop() - { - return Task.CompletedTask; - } - - public void Dispose() - { - _redlockFactory?.Dispose(); - } - - } -} \ No newline at end of file diff --git a/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj b/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj deleted file mode 100644 index dc7c6c292..000000000 --- a/src/providers/WorkflowCore.LockProviders.Redlock/WorkflowCore.LockProviders.Redlock.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - Workflow Core Redlock distributed lock manager - 1.6.0 - Daniel Gerlag - netstandard1.3 - WorkflowCore.LockProviders.Redlock - WorkflowCore.LockProviders.Redlock - workflow;.NET;Core;state machine;WorkflowCore;Redlock - https://github.com/danielgerlag/workflow-core - https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md - git - https://github.com/danielgerlag/workflow-core.git - 1.6.1 - $(PackageTargetFallback);dnxcore50 - false - false - false - 1.7.0 - - Distributed lock provider for Workflow-core using Redis - 1.7.0.0 - 1.7.0.0 - - - - - - - - - - - - - - - - - diff --git a/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj b/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj index 13a9d87ab..ceb51046d 100644 --- a/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj @@ -1,15 +1,15 @@  - netstandard1.3 + netstandard2.0 Distributed lock provider for Workflow-core using SQL Server https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md - 1.7.0 + 2.0.0 - + diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index b8ecefc08..bd48d1a2d 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -2,7 +2,6 @@ Workflow Core EntityFramework Core Persistence Provider - 1.8.0 Daniel Gerlag netstandard2.0 WorkflowCore.Persistence.EntityFramework @@ -15,10 +14,10 @@ false false false - 1.9.1 + 2.0.0 Base package for Workflow-core peristence providers using entity framework - 1.9.1.0 - 1.9.1.0 + 2.0.0.0 + 2.0.0.0 diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 32ce70155..f4a94d337 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -2,9 +2,8 @@ Workflow Core MongoDB Persistence Provider - 1.1.0 Daniel Gerlag - netstandard1.5 + netstandard2.0 WorkflowCore.Persistence.MongoDB WorkflowCore.Persistence.MongoDB workflow;.NET;Core;state machine;WorkflowCore;MongoDB;Mongo @@ -12,15 +11,13 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 1.6.1 - $(PackageTargetFallback);dnxcore50 false false false - 1.9.0 + 2.0.0 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 1.9.0.0 - 1.9.0.0 + 2.0.0.0 + 2.0.0.0 @@ -28,7 +25,7 @@ - + diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 4524afe6f..e210a8b55 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -2,7 +2,6 @@ Workflow Core PostgreSQL Persistence Provider - 1.8.0 Daniel Gerlag netstandard2.0 WorkflowCore.Persistence.PostgreSQL @@ -16,9 +15,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 1.9.1 - 1.9.1.0 - 1.9.1.0 + 2.0.0 + 2.0.0.0 + 2.0.0.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 432d8f1d4..00dc7ae06 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,10 +15,10 @@ false false false - 1.9.1 + 2.0.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 1.9.1.0 - 1.9.1.0 + 2.0.0.0 + 2.0.0.0 diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 3d44d7cd0..ad4e4b15b 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,15 +11,15 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 1.9.1 - 1.9.1.0 + 2.0.0 + 2.0.0.0 - + diff --git a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj index c8dfff7f9..8b67cbfc9 100644 --- a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj +++ b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj @@ -1,21 +1,21 @@  - netstandard1.3 + netstandard2.0 Azure providers for Workflow Core - Provides distributed lock management on Workflow Core - Provides Queueing support on Workflow Core workflow workflowcore dlm - 1.9.0 + 2.0.0 $(PackageTargetFallback);dnxcore50 https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git Daniel Gerlag - 1.9.0.0 - 1.9.0.0 + 2.0.0.0 + 2.0.0.0 diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index a38d3dda5..2f66017f3 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.9.0 + 2.0.0 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git @@ -13,7 +13,7 @@ - + diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index 994f2aebd..e4489cb54 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.9.1 + 2.0.0 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git @@ -12,7 +12,7 @@ - + diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj index 8cb58ca42..6735f21eb 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj @@ -4,7 +4,7 @@ Workflow Core RabbitMQ queue provider 1.1.0 Daniel Gerlag - netstandard1.3 + netstandard2.0 WorkflowCore.QueueProviders.RabbitMQ WorkflowCore.QueueProviders.RabbitMQ workflow;.NET;Core;state machine;WorkflowCore;RabbitMQ @@ -12,15 +12,13 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 1.6.1 - $(PackageTargetFallback);dnxcore50 false false false - 1.9.0 + 2.0.0 Queue provider for Workflow-core using RabbitMQ - 1.9.0.0 - 1.9.0.0 + 2.0.0.0 + 2.0.0.0 diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 131a20359..205907c87 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -20,9 +20,9 @@ - + - + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index bcbe56b99..1b994fbd8 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 WorkflowCore.Sample04 Exe WorkflowCore.Sample04 diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index 989389d1e..fef512688 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 true WorkflowCore.Sample07 Exe diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 2d05c5ed1..2f99c96b2 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 ScratchPad Exe ScratchPad diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs similarity index 96% rename from test/WorkflowCore.IntegrationTests/Scenarios/StoredScenario.cs rename to test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs index e854c45cb..fcc50b555 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs @@ -10,9 +10,9 @@ namespace WorkflowCore.IntegrationTests.Scenarios { - public class StoredScenario : JsonWorkflowTest + public class StoredJsonScenario : JsonWorkflowTest { - public StoredScenario() + public StoredJsonScenario() { Setup(); } diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs new file mode 100644 index 000000000..820f86bd8 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using WorkflowCore.Testing; +using WorkflowCore.TestAssets.DataTypes; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class StoredYamlScenario : YamlWorkflowTest + { + public StoredYamlScenario() + { + Setup(); + } + + [Fact(DisplayName = "Execute workflow from stored YAML definition")] + public void should_execute_yaml_workflow() + { + var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionYaml(), new CounterBoard() { Flag1 = true, Flag2 = true }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + var data = GetData(workflowId); + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + data.Counter1.Should().Be(1); + data.Counter2.Should().Be(1); + data.Counter3.Should().Be(1); + data.Counter4.Should().Be(1); + data.Counter5.Should().Be(0); + data.Counter6.Should().Be(1); + } + } +} diff --git a/test/WorkflowCore.TestAssets/Properties/Resources.Designer.cs b/test/WorkflowCore.TestAssets/Properties/Resources.Designer.cs index 83ec55dd1..286c37d49 100644 --- a/test/WorkflowCore.TestAssets/Properties/Resources.Designer.cs +++ b/test/WorkflowCore.TestAssets/Properties/Resources.Designer.cs @@ -10,7 +10,6 @@ namespace WorkflowCore.TestAssets.Properties { using System; - using System.Reflection; /// @@ -40,7 +39,7 @@ internal Resources() { public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WorkflowCore.TestAssets.Properties.Resources", typeof(Resources).GetTypeInfo().Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WorkflowCore.TestAssets.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; @@ -71,21 +70,28 @@ internal Resources() { /// { /// "Id": "Step1", /// "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", - /// - /// "NextStepId": "Generate" + /// "ErrorBehavior": "Retry", + /// "Inputs": { "Value": "data.Counter1" }, + /// "Outputs": { "Counter1": "step.Value" }, + /// "NextStepId": "Step2" /// }, /// { - /// "Id": "Generate", - /// "StepType": "ScratchPad.GenerateMessage, ScratchPad", - /// "NextStepId": "Print", - /// "Outputs": { "Value3": "step.Message" } - /// }, - /// { - /// "I [rest of string was truncated]";. + /// "Id": "Step2", + /// "StepType": "WorkflowCore.TestAsset [rest of string was truncated]";. + /// + public static string stored_definition_json { + get { + return ResourceManager.GetString("stored_definition_json", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. /// - public static string stored_definition { + public static byte[] stored_definition_yaml { get { - return ResourceManager.GetString("stored_definition", resourceCulture); + object obj = ResourceManager.GetObject("stored_definition_yaml", resourceCulture); + return ((byte[])(obj)); } } } diff --git a/test/WorkflowCore.TestAssets/Properties/Resources.resx b/test/WorkflowCore.TestAssets/Properties/Resources.resx index 78ec93e80..5ed7638ee 100644 --- a/test/WorkflowCore.TestAssets/Properties/Resources.resx +++ b/test/WorkflowCore.TestAssets/Properties/Resources.resx @@ -118,7 +118,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + ..\stored-definition.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + ..\stored-definition.yaml;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/test/WorkflowCore.TestAssets/Utils.cs b/test/WorkflowCore.TestAssets/Utils.cs index c4f5cae5a..62875e7c1 100644 --- a/test/WorkflowCore.TestAssets/Utils.cs +++ b/test/WorkflowCore.TestAssets/Utils.cs @@ -20,10 +20,14 @@ public static T DeepCopy(T obj) public static string GetTestDefinitionJson() { - //return Properties.Resources.ResourceManager.GetString("stored_definition"); return File.ReadAllText("stored-definition.json"); } + public static string GetTestDefinitionYaml() + { + return File.ReadAllText("stored-definition.yaml"); + } + public static string GetTestDefinitionDynamicJson() { return File.ReadAllText("stored-dynamic-definition.json"); diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index eb4ea7f20..541f2afa9 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -1,11 +1,9 @@  - netstandard1.6 + netstandard2.0 WorkflowCore.TestAssets WorkflowCore.TestAssets - 1.6.1 - $(PackageTargetFallback);dnxcore50 false false false @@ -13,10 +11,14 @@ + + + Always + Always @@ -30,7 +32,7 @@ - + diff --git a/test/WorkflowCore.TestAssets/stored-definition.yaml b/test/WorkflowCore.TestAssets/stored-definition.yaml new file mode 100644 index 000000000..f01efb07b --- /dev/null +++ b/test/WorkflowCore.TestAssets/stored-definition.yaml @@ -0,0 +1,58 @@ +Id: Test +Version: 1 +DataType: WorkflowCore.TestAssets.DataTypes.CounterBoard, WorkflowCore.TestAssets +Steps: +- Id: Step1 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + ErrorBehavior: Retry + Inputs: + Value: data.Counter1 + Outputs: + Counter1: step.Value + NextStepId: Step2 +- Id: Step2 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter2 + Outputs: + Counter2: step.Value + NextStepId: Step3 +- Id: Step3 + StepType: WorkflowCore.Primitives.If, WorkflowCore + NextStepId: Step4 + Inputs: + Condition: data.Flag1 + Do: + - - Id: Step3.1.1 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter3 + Outputs: + Counter3: step.Value + NextStepId: Step3.1.2 + - Id: Step3.1.2 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter4 + Outputs: + Counter4: step.Value + - - Id: Step3.2.1 + StepType: WorkflowCore.Primitives.WaitFor, WorkflowCore + NextStepId: Step3.2.2 + CancelCondition: data.Flag2 + Inputs: + EventName: '"Event1"' + EventKey: '"Key1"' + EffectiveDate: DateTime.Now + - Id: Step3.2.2 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter5 + Outputs: + Counter5: step.Value +- Id: Step4 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter6 + Outputs: + Counter6: step.Value diff --git a/test/WorkflowCore.Testing/JsonWorkflowTest.cs b/test/WorkflowCore.Testing/JsonWorkflowTest.cs index 0f15505cd..78f36eae8 100644 --- a/test/WorkflowCore.Testing/JsonWorkflowTest.cs +++ b/test/WorkflowCore.Testing/JsonWorkflowTest.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.Models; +using WorkflowCore.Services.DefinitionStorage; namespace WorkflowCore.Testing { @@ -55,7 +56,7 @@ protected virtual void ConfigureServices(IServiceCollection services) public string StartWorkflow(string json, object data) { - var def = DefinitionLoader.LoadDefinition(json); + var def = DefinitionLoader.LoadDefinition(json, Deserializers.Json); var workflowId = Host.StartWorkflow(def.Id, data).Result; return workflowId; } diff --git a/test/WorkflowCore.Testing/YamlWorkflowTest.cs b/test/WorkflowCore.Testing/YamlWorkflowTest.cs new file mode 100644 index 000000000..6bd524e60 --- /dev/null +++ b/test/WorkflowCore.Testing/YamlWorkflowTest.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Services.DefinitionStorage; + +namespace WorkflowCore.Testing +{ + public abstract class YamlWorkflowTest : IDisposable + { + protected IWorkflowHost Host; + protected IPersistenceProvider PersistenceProvider; + protected IDefinitionLoader DefinitionLoader; + protected List UnhandledStepErrors = new List(); + + protected virtual void Setup() + { + //setup dependency injection + IServiceCollection services = new ServiceCollection(); + services.AddLogging(); + ConfigureServices(services); + + var serviceProvider = services.BuildServiceProvider(); + + //config logging + var loggerFactory = serviceProvider.GetService(); + //loggerFactory.AddConsole(LogLevel.Debug); + + PersistenceProvider = serviceProvider.GetService(); + DefinitionLoader = serviceProvider.GetService(); + Host = serviceProvider.GetService(); + Host.OnStepError += Host_OnStepError; + Host.Start(); + } + + private void Host_OnStepError(WorkflowInstance workflow, WorkflowStep step, Exception exception) + { + UnhandledStepErrors.Add(new StepError() + { + Exception = exception, + Step = step, + Workflow = workflow + }); + } + + protected virtual void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(); + } + + public string StartWorkflow(string json, object data) + { + var def = DefinitionLoader.LoadDefinition(json, Deserializers.Yaml); + var workflowId = Host.StartWorkflow(def.Id, data).Result; + return workflowId; + } + + protected void WaitForWorkflowToComplete(string workflowId, TimeSpan timeOut) + { + var status = GetStatus(workflowId); + var counter = 0; + while ((status == WorkflowStatus.Runnable) && (counter < (timeOut.TotalMilliseconds / 100))) + { + Thread.Sleep(100); + counter++; + status = GetStatus(workflowId); + } + } + + protected IEnumerable GetActiveSubscriptons(string eventName, string eventKey) + { + return PersistenceProvider.GetSubcriptions(eventName, eventKey, DateTime.MaxValue).Result; + } + + protected void WaitForEventSubscription(string eventName, string eventKey, TimeSpan timeOut) + { + var counter = 0; + while ((!GetActiveSubscriptons(eventName, eventKey).Any()) && (counter < (timeOut.TotalMilliseconds / 100))) + { + Thread.Sleep(100); + counter++; + } + } + + protected WorkflowStatus GetStatus(string workflowId) + { + var instance = PersistenceProvider.GetWorkflowInstance(workflowId).Result; + return instance.Status; + } + + protected TData GetData(string workflowId) + { + var instance = PersistenceProvider.GetWorkflowInstance(workflowId).Result; + return (TData)instance.Data; + } + + public void Dispose() + { + Host.Stop(); + } + } + +} diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index bc41b01d9..884e2937e 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -27,7 +27,7 @@ - + diff --git a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs index 99a9b49ce..03494c5de 100644 --- a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs +++ b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs @@ -29,7 +29,7 @@ public DefinitionLoaderTests() [Fact(DisplayName = "Should register workflow")] public void RegisterDefinition() { - _subject.LoadDefinition("{\"Id\": \"HelloWorld\", \"Version\": 1, \"Steps\": []}"); + _subject.LoadDefinition("{\"Id\": \"HelloWorld\", \"Version\": 1, \"Steps\": []}", Deserializers.Json); A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Id == "HelloWorld"))).MustHaveHappened(); A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Version == 1))).MustHaveHappened(); @@ -39,7 +39,7 @@ public void RegisterDefinition() [Fact(DisplayName = "Should parse definition")] public void ParseDefinition() { - _subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionJson()); + _subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionJson(), Deserializers.Json); A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Id == "Test"))).MustHaveHappened(); A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Version == 1))).MustHaveHappened(); @@ -51,7 +51,7 @@ public void ParseDefinition() [Fact(DisplayName = "Should parse definition")] public void ParseDefinitionDynamic() { - _subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionDynamicJson()); + _subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionDynamicJson(), Deserializers.Json); A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Id == "Test"))).MustHaveHappened(); A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Version == 1))).MustHaveHappened(); From cfa5db7375638daafc0d4b2e6e0ba89cb15c2acc Mon Sep 17 00:00:00 2001 From: Szabolcs Havasfalvi Date: Thu, 4 Jul 2019 20:08:28 +0200 Subject: [PATCH 135/462] Typo in a log message has been fixed (WorkflowHost.cs) --- src/WorkflowCore/Services/WorkflowHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index 1230ac97f..556b982b0 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -86,7 +86,7 @@ public void Start() _lifeCycleEventHub.Start().Wait(); _searchIndex.Start().Wait(); - Logger.LogInformation("Starting backgroud tasks"); + Logger.LogInformation("Starting background tasks"); foreach (var task in _backgroundTasks) task.Start(); From 8094a5a822d4db4d7a0256ea198de778f0ddf4a3 Mon Sep 17 00:00:00 2001 From: "am.mirzaei" Date: Mon, 8 Jul 2019 11:59:02 +0430 Subject: [PATCH 136/462] Exception object were added to LogError function of loggerFactory objects. --- src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs | 2 +- src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs | 2 +- src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs | 2 +- src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs | 4 ++-- .../Services/BackgroundTasks/WorkflowConsumer.cs | 2 +- src/WorkflowCore/Services/CancellationProcessor.cs | 2 +- src/WorkflowCore/Services/WorkflowExecutor.cs | 2 +- .../Services/AzureLockManager.cs | 6 +++--- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index 49fc847cd..71f6a9c86 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -100,7 +100,7 @@ private async Task SeedSubscription(Event evt, EventSubscription sub, List } catch (Exception ex) { - Logger.LogError(ex.Message); + Logger.LogError(ex, ex.Message); return false; } finally diff --git a/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs index 661fad789..10899af10 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs @@ -68,7 +68,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance _errorCounts.Remove(itemId); } - Logger.LogError(default(EventId), $"Unable to index workfow - {itemId} - {e.Message}"); + Logger.LogError(default(EventId), e, $"Unable to index workfow - {itemId} - {e.Message}"); } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 959ab24b1..2ff9c275d 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -113,7 +113,7 @@ private async void Execute() } catch (Exception ex) { - Logger.LogError(ex.Message); + Logger.LogError(ex, ex.Message); } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs index 22c136ad1..559c123f0 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs @@ -67,7 +67,7 @@ private async void PollRunnables(object target) } catch (Exception ex) { - _logger.LogError(ex.Message); + _logger.LogError(ex, ex.Message); } try @@ -92,7 +92,7 @@ private async void PollRunnables(object target) } catch (Exception ex) { - _logger.LogError(ex.Message); + _logger.LogError(ex, ex.Message); } } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index de8e7ad4a..4cfa74716 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -119,7 +119,7 @@ private async void FutureQueue(WorkflowInstance workflow, CancellationToken canc } catch (Exception ex) { - Logger.LogError(ex.Message); + Logger.LogError(ex, ex.Message); } } } diff --git a/src/WorkflowCore/Services/CancellationProcessor.cs b/src/WorkflowCore/Services/CancellationProcessor.cs index e75d94f05..74c7a8a60 100644 --- a/src/WorkflowCore/Services/CancellationProcessor.cs +++ b/src/WorkflowCore/Services/CancellationProcessor.cs @@ -31,7 +31,7 @@ public void ProcessCancellations(WorkflowInstance workflow, WorkflowDefinition w } catch (Exception ex) { - _logger.LogError(default(EventId), ex.Message, ex); + _logger.LogError(default(EventId), ex, ex.Message); } if (cancel) { diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index ce52e26b7..9c28ce7f3 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -80,7 +80,7 @@ public async Task Execute(WorkflowInstance workflow) } catch (Exception ex) { - _logger.LogError("Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); + _logger.LogError(ex, "Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs b/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs index 2bc97ec67..3d718b6e9 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs @@ -74,7 +74,7 @@ public async Task ReleaseLock(string Id) } catch (Exception ex) { - _logger.LogError($"Error releasing lock - {ex.Message}"); + _logger.LogError(ex, $"Error releasing lock - {ex.Message}"); } _locks.Remove(entry); } @@ -114,7 +114,7 @@ private async void RenewLeases(object state) } catch (Exception ex) { - _logger.LogError($"Error renewing leases - {ex.Message}"); + _logger.LogError(ex, $"Error renewing leases - {ex.Message}"); } finally { @@ -131,7 +131,7 @@ private async Task RenewLock(ControlledLock entry) } catch (Exception ex) { - _logger.LogError($"Error renewing lease - {ex.Message}"); + _logger.LogError(ex, $"Error renewing lease - {ex.Message}"); } } } From 370fa1b62907fd5fbbe598a266c256f6545d1fe7 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Wed, 10 Jul 2019 13:49:27 -0500 Subject: [PATCH 137/462] Adding null check for missing input step property name when loading definition from storage to give better error message. --- .../DefinitionStorage/DefinitionLoader.cs | 13 +++++++++---- test/WorkflowCore.TestAssets/Utils.cs | 7 ++++++- .../WorkflowCore.TestAssets.csproj | 3 +++ .../stored-definition-input-exc.json | 19 +++++++++++++++++++ .../DefinitionLoaderTests.cs | 7 ++++++- 5 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 test/WorkflowCore.TestAssets/stored-definition-input-exc.json diff --git a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs index 9d37dd9da..5483e0851 100644 --- a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs +++ b/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs @@ -25,7 +25,7 @@ public DefinitionLoader(IWorkflowRegistry registry) { _registry = registry; } - + public WorkflowDefinition LoadDefinition(string source, Func deserializer) { var sourceObj = deserializer(source); @@ -119,7 +119,7 @@ private WorkflowStepCollection ConvertSteps(ICollection source, Ty targetStep.Outcomes.Add(new StepOutcome() { ExternalNextStepId = $"{nextStep.NextStepId}" }); result.Add(targetStep); - + i++; } @@ -176,6 +176,11 @@ private void AttachInputs(StepSourceV1 source, Type dataType, Type stepType, Wor var environmentVarsParameter = Expression.Parameter(typeof(IDictionary), "environment"); var stepProperty = stepType.GetProperty(input.Key); + if (stepProperty == null) + { + throw new ArgumentException($"Unknown property for input {input.Key} on {source.Id}"); + } + if (input.Value is string) { var acn = BuildScalarInputAction(input, dataParameter, contextParameter, environmentVarsParameter, stepProperty); @@ -203,7 +208,7 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo var dataParameter = Expression.Parameter(dataType, "data"); Expression targetProperty; - + // Check if our datatype has a matching property var propertyInfo = dataType.GetProperty(output.Key); if (propertyInfo != null) @@ -217,7 +222,7 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo // If we did not find a matching property try to find a Indexer with string parameter propertyInfo = dataType.GetProperty("Item"); targetProperty = Expression.Property(dataParameter, propertyInfo, Expression.Constant(output.Key)); - + Action acn = (pStep, pData) => { object resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); ; diff --git a/test/WorkflowCore.TestAssets/Utils.cs b/test/WorkflowCore.TestAssets/Utils.cs index 62875e7c1..fdf7541f2 100644 --- a/test/WorkflowCore.TestAssets/Utils.cs +++ b/test/WorkflowCore.TestAssets/Utils.cs @@ -10,7 +10,7 @@ namespace WorkflowCore.TestAssets public static class Utils { private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All, DateFormatHandling = DateFormatHandling.IsoDateFormat, DateTimeZoneHandling = DateTimeZoneHandling.Utc }; - + public static T DeepCopy(T obj) { string str = JsonConvert.SerializeObject(obj, SerializerSettings); @@ -32,6 +32,11 @@ public static string GetTestDefinitionDynamicJson() { return File.ReadAllText("stored-dynamic-definition.json"); } + + public static string GetTestDefinitionJsonInputExc() + { + return File.ReadAllText("stored-definition-input-exc.json"); + } } } diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index 541f2afa9..d01b8f985 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -25,6 +25,9 @@ Always + + Always + diff --git a/test/WorkflowCore.TestAssets/stored-definition-input-exc.json b/test/WorkflowCore.TestAssets/stored-definition-input-exc.json new file mode 100644 index 000000000..dbf43105a --- /dev/null +++ b/test/WorkflowCore.TestAssets/stored-definition-input-exc.json @@ -0,0 +1,19 @@ +{ + "Id": "Test", + "Version": 1, + "Description": "", + "DataType": "WorkflowCore.TestAssets.DataTypes.CounterBoard, WorkflowCore.TestAssets", + "Steps": [ + { + "Id": "Step1", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "ErrorBehavior": "Retry", + "Inputs": { + "Value1": "data.Counter1" + }, + "Outputs": { + "Counter1": "step.Value" + } + } + ] +} \ No newline at end of file diff --git a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs index 03494c5de..3e093f0f9 100644 --- a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs +++ b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs @@ -59,13 +59,18 @@ public void ParseDefinitionDynamic() A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(MatchTestDefinition, ""))).MustHaveHappened(); } + [Fact(DisplayName = "Should throw error for bad input property name on step")] + public void ParseDefinitionInputException() + { + Assert.Throws(() => _subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionJsonInputExc(), Deserializers.Json)); + } private bool MatchTestDefinition(WorkflowDefinition def) { //TODO: make this better var step1 = def.Steps.Single(s => s.ExternalId == "Step1"); var step2 = def.Steps.Single(s => s.ExternalId == "Step2"); - + step1.Outcomes.Count.Should().Be(1); step1.Inputs.Count.Should().Be(1); step1.Outputs.Count.Should().Be(1); From 283454baa5b645ef200823f443493174a6a60b91 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 17 Jul 2019 20:21:26 -0700 Subject: [PATCH 138/462] Update README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c295eeee6..11bd9b25b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,13 @@ Workflow Core is a light weight workflow engine targeting .NET Standard. Think: long running processes with multiple tasks that need to track state. It supports pluggable persistence and concurrency providers to allow for multi-node clusters. +### Announcements + +#### New related project: Conductor +Conductor is a stand-alone workflow server as opposed to a library that uses Workflow Core internally. It exposes an API that allows you to store workflow definitions, track running workflows, manage events and define lambdas and scripts for usage in your workflows. + +https://github.com/danielgerlag/conductor + ## Documentation See [Tutorial here.](https://github.com/danielgerlag/workflow-core/wiki) @@ -187,12 +194,15 @@ These are also available as separate Nuget packages. * **Aaron Scribner** * **Roberto Paterlini** +# Related Projects + +* [Conductor](https://github.com/danielgerlag/conductor) (Stand-alone workflow server built on Workflow Core) + ## Ports * [JWorkflow (Java)](https://github.com/danielgerlag/jworkflow) * [workflow-es (Node.js)](https://github.com/danielgerlag/workflow-es) * [liteflow (Python)](https://github.com/danielgerlag/liteflow) -* [workflow_rb (Ruby)](https://github.com/danielgerlag/workflow_rb) ## License From 8984eea8227bf36d3727754a104fc69c4917fa85 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 17 Jul 2019 20:22:00 -0700 Subject: [PATCH 139/462] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11bd9b25b..f750eff44 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ These are also available as separate Nuget packages. * **Aaron Scribner** * **Roberto Paterlini** -# Related Projects +## Related Projects * [Conductor](https://github.com/danielgerlag/conductor) (Stand-alone workflow server built on Workflow Core) From dd70ec4a13ca04abb2d9ef005a0e83211d87143c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 21 Jul 2019 20:14:12 -0700 Subject: [PATCH 140/462] update exec pointers for EF providers --- .../ExtensionMethods.cs | 3 +-- .../WorkflowCore.Persistence.EntityFramework.csproj | 6 +++--- .../WorkflowCore.Persistence.PostgreSQL.csproj | 6 +++--- .../WorkflowCore.Persistence.SqlServer.csproj | 6 +++--- .../WorkflowCore.Persistence.Sqlite.csproj | 6 +++--- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs index f7173ce75..c1f1ad875 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs @@ -26,8 +26,7 @@ internal static PersistedWorkflow ToPersistable(this WorkflowInstance instance, persistable.WorkflowDefinitionId = instance.WorkflowDefinitionId; persistable.Status = instance.Status; persistable.CreateTime = instance.CreateTime; - persistable.CompleteTime = instance.CompleteTime; - persistable.ExecutionPointers = new PersistedExecutionPointerCollection(instance.ExecutionPointers.Count); + persistable.CompleteTime = instance.CompleteTime; foreach (var ep in instance.ExecutionPointers) { diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index bd48d1a2d..1f1c8340d 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -14,10 +14,10 @@ false false false - 2.0.0 + 2.0.1 Base package for Workflow-core peristence providers using entity framework - 2.0.0.0 - 2.0.0.0 + 2.0.1.0 + 2.0.1.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index e210a8b55..484dc6941 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,9 +15,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 2.0.0 - 2.0.0.0 - 2.0.0.0 + 2.0.1 + 2.0.1.0 + 2.0.1.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 00dc7ae06..d7076d514 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,10 +15,10 @@ false false false - 2.0.0 + 2.0.1 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 2.0.0.0 - 2.0.0.0 + 2.0.1.0 + 2.0.1.0 diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index dd4fce079..fa97a4a7d 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 1.9.1 - 1.9.1.0 - 1.9.1.0 + 1.9.2 + 1.9.2.0 + 1.9.2.0 From dca94dfa7a1920ac61eb1e49c67560bbc7dfadd5 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Tue, 23 Jul 2019 11:11:14 -0500 Subject: [PATCH 141/462] Renamed stored definition test file to be more descriptive. --- test/WorkflowCore.TestAssets/Utils.cs | 4 ++-- test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj | 2 +- ...-input-exc.json => stored-def-missing-input-property.json} | 0 .../Services/DefinitionStorage/DefinitionLoaderTests.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename test/WorkflowCore.TestAssets/{stored-definition-input-exc.json => stored-def-missing-input-property.json} (100%) diff --git a/test/WorkflowCore.TestAssets/Utils.cs b/test/WorkflowCore.TestAssets/Utils.cs index fdf7541f2..ea0f85f54 100644 --- a/test/WorkflowCore.TestAssets/Utils.cs +++ b/test/WorkflowCore.TestAssets/Utils.cs @@ -33,9 +33,9 @@ public static string GetTestDefinitionDynamicJson() return File.ReadAllText("stored-dynamic-definition.json"); } - public static string GetTestDefinitionJsonInputExc() + public static string GetTestDefinitionJsonMissingInputProperty() { - return File.ReadAllText("stored-definition-input-exc.json"); + return File.ReadAllText("stored-def-missing-input-property.json"); } } } diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index d01b8f985..0105a6e9d 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -25,7 +25,7 @@ Always - + Always diff --git a/test/WorkflowCore.TestAssets/stored-definition-input-exc.json b/test/WorkflowCore.TestAssets/stored-def-missing-input-property.json similarity index 100% rename from test/WorkflowCore.TestAssets/stored-definition-input-exc.json rename to test/WorkflowCore.TestAssets/stored-def-missing-input-property.json diff --git a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs index 3e093f0f9..0a9293523 100644 --- a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs +++ b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs @@ -62,7 +62,7 @@ public void ParseDefinitionDynamic() [Fact(DisplayName = "Should throw error for bad input property name on step")] public void ParseDefinitionInputException() { - Assert.Throws(() => _subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionJsonInputExc(), Deserializers.Json)); + Assert.Throws(() => _subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionJsonMissingInputProperty(), Deserializers.Json)); } private bool MatchTestDefinition(WorkflowDefinition def) From ddfd7e96419a129c5acff0753497bdd4ceeba092 Mon Sep 17 00:00:00 2001 From: Poorya Date: Wed, 24 Jul 2019 17:46:22 +0200 Subject: [PATCH 142/462] parameterize postgres provider schema name --- .../MigrationContextFactory.cs | 2 +- .../PostgresContext.cs | 16 +++++++++------- .../PostgresContextFactory.cs | 6 ++++-- .../ServiceCollectionExtensions.cs | 5 +++-- .../PostgresPersistenceProviderFixture.cs | 2 +- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/MigrationContextFactory.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/MigrationContextFactory.cs index 582034d42..4424b01cd 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/MigrationContextFactory.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/MigrationContextFactory.cs @@ -7,7 +7,7 @@ public class MigrationContextFactory : IDesignTimeDbContextFactory builder) { - builder.ToTable("Subscription", "wfc"); + builder.ToTable("Subscription", _schemaName); builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); } protected override void ConfigureWorkflowStorage(EntityTypeBuilder builder) { - builder.ToTable("Workflow", "wfc"); + builder.ToTable("Workflow", _schemaName); builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); } protected override void ConfigureExecutionPointerStorage(EntityTypeBuilder builder) { - builder.ToTable("ExecutionPointer", "wfc"); + builder.ToTable("ExecutionPointer", _schemaName); builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); } protected override void ConfigureExecutionErrorStorage(EntityTypeBuilder builder) { - builder.ToTable("ExecutionError", "wfc"); + builder.ToTable("ExecutionError", _schemaName); builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); } protected override void ConfigureExetensionAttributeStorage(EntityTypeBuilder builder) { - builder.ToTable("ExtensionAttribute", "wfc"); + builder.ToTable("ExtensionAttribute", _schemaName); builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); } protected override void ConfigureEventStorage(EntityTypeBuilder builder) { - builder.ToTable("Event", "wfc"); + builder.ToTable("Event", _schemaName); builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); } } diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs index de6d0e24d..e0758dfab 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs @@ -9,15 +9,17 @@ namespace WorkflowCore.Persistence.PostgreSQL public class PostgresContextFactory : IWorkflowDbContextFactory { private readonly string _connectionString; + private readonly string _schemaName; - public PostgresContextFactory(string connectionString) + public PostgresContextFactory(string connectionString, string schemaName) { _connectionString = connectionString; + _schemaName = schemaName; } public WorkflowDbContext Build() { - return new PostgresContext(_connectionString); + return new PostgresContext(_connectionString,_schemaName); } } } diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs index 26abb29ce..bf1bac81f 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs @@ -10,9 +10,10 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { - public static WorkflowOptions UsePostgreSQL(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB) + public static WorkflowOptions UsePostgreSQL(this WorkflowOptions options, + string connectionString, bool canCreateDB, bool canMigrateDB, string schemaName="wfc") { - options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new PostgresContextFactory(connectionString), canCreateDB, canMigrateDB)); + options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new PostgresContextFactory(connectionString, schemaName), canCreateDB, canMigrateDB)); return options; } } diff --git a/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs index 21215f61d..95ebe7976 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs @@ -19,7 +19,7 @@ public class PostgresPersistenceProviderFixture : BasePersistenceFixture public PostgresPersistenceProviderFixture(PostgresDockerSetup dockerSetup, ITestOutputHelper output) { output.WriteLine($"Connecting on {PostgresDockerSetup.ConnectionString}"); - _subject = new EntityFrameworkPersistenceProvider(new PostgresContextFactory(PostgresDockerSetup.ConnectionString), true, true); + _subject = new EntityFrameworkPersistenceProvider(new PostgresContextFactory(PostgresDockerSetup.ConnectionString,"wfc"), true, true); _subject.EnsureStoreExists(); } } From 1a13a712c5b075af133d3bd649feb9e7d5799b39 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 28 Jul 2019 08:04:59 -0700 Subject: [PATCH 143/462] initial mkdocs documentation (#357) --- docs/docs/control-structures.md | 133 +++++++++ docs/docs/error-handling.md | 15 + docs/docs/extensions.md | 3 + docs/docs/external-events.md | 26 ++ docs/docs/getting-started.md | 270 +++++++++++++++++ docs/docs/index.md | 34 +++ docs/docs/json-yaml.md | 444 ++++++++++++++++++++++++++++ docs/docs/multi-node-clusters.md | 20 ++ docs/docs/persistence.md | 12 + docs/docs/sagas.md | 161 ++++++++++ docs/docs/samples.md | 33 +++ docs/docs/using-with-aspnet-core.md | 47 +++ docs/docs/wip/multiple-outcomes.md | 21 ++ docs/docs/wip/steps-deep.md | 16 + docs/mkdocs.yml | 15 + 15 files changed, 1250 insertions(+) create mode 100644 docs/docs/control-structures.md create mode 100644 docs/docs/error-handling.md create mode 100644 docs/docs/extensions.md create mode 100644 docs/docs/external-events.md create mode 100644 docs/docs/getting-started.md create mode 100644 docs/docs/index.md create mode 100644 docs/docs/json-yaml.md create mode 100644 docs/docs/multi-node-clusters.md create mode 100644 docs/docs/persistence.md create mode 100644 docs/docs/sagas.md create mode 100644 docs/docs/samples.md create mode 100644 docs/docs/using-with-aspnet-core.md create mode 100644 docs/docs/wip/multiple-outcomes.md create mode 100644 docs/docs/wip/steps-deep.md create mode 100644 docs/mkdocs.yml diff --git a/docs/docs/control-structures.md b/docs/docs/control-structures.md new file mode 100644 index 000000000..0c49705b8 --- /dev/null +++ b/docs/docs/control-structures.md @@ -0,0 +1,133 @@ +# Control Structures + +## Parallel ForEach + +Use the .ForEach method to start a parallel for loop + +```C# +public class ForEachWorkflow : IWorkflow +{ + public string Id => "Foreach"; + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .ForEach(data => new List() { 1, 2, 3, 4 }) + .Do(x => x + .StartWith() + .Input(step => step.Message, (data, context) => context.Item) + .Then()) + .Then(); + } +} +``` + +## While Loops + +Use the .While method to start a while construct + +```C# +public class WhileWorkflow : IWorkflow +{ + public string Id => "While"; + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .While(data => data.Counter < 3) + .Do(x => x + .StartWith() + .Then() + .Input(step => step.Value1, data => data.Counter) + .Output(data => data.Counter, step => step.Value2)) + .Then(); + } +} +``` + +## If Conditions + +Use the .If method to start an if condition + +```C# +public class IfWorkflow : IWorkflow +{ + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .If(data => data.Counter < 3).Do(then => then + .StartWith() + .Input(step => step.Message, data => "Value is less than 3") + ) + .If(data => data.Counter < 5).Do(then => then + .StartWith() + .Input(step => step.Message, data => "Value is less than 5") + ) + .Then(); + } +} +``` + +## Parallel Paths + +Use the .Parallel() method to branch parallel tasks + +```C# +public class ParallelWorkflow : IWorkflow +{ + public string Id => "parallel-sample"; + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Parallel() + .Do(then => + then.StartWith() + .Then() + .Do(then => + then.StartWith() + .Then() + .Do(then => + then.StartWith() + .Then() + .Join() + .Then(); + } +} +``` + +## Schedule + +Use `.Schedule` to register a future set of steps to run asynchronously in the background within your workflow. + + +```c# +builder + .StartWith(context => Console.WriteLine("Hello")) + .Schedule(data => TimeSpan.FromSeconds(5)).Do(schedule => schedule + .StartWith(context => Console.WriteLine("Doing scheduled tasks")) + ) + .Then(context => Console.WriteLine("Doing normal tasks")); +``` + + +## Recur + +Use `.Recur` to setup a set of recurring background steps within your workflow, until a certain condition is met + + +```c# +builder + .StartWith(context => Console.WriteLine("Hello")) + .Recur(data => TimeSpan.FromSeconds(5), data => data.Counter > 5).Do(recur => recur + .StartWith(context => Console.WriteLine("Doing recurring task")) + ) + .Then(context => Console.WriteLine("Carry on")); +``` diff --git a/docs/docs/error-handling.md b/docs/docs/error-handling.md new file mode 100644 index 000000000..1e7554f7a --- /dev/null +++ b/docs/docs/error-handling.md @@ -0,0 +1,15 @@ +# Error handling + +Each step can be configured with it's own error handling behavior, it can be retried at a later time, suspend the workflow or terminate the workflow. + +```C# +public void Build(IWorkflowBuilder builder) +{ + builder + .StartWith() + .OnError(WorkflowErrorHandling.Retry, TimeSpan.FromMinutes(10)) + .Then(); +} +``` + +The WorkflowHost service also has a .OnStepError event which can be used to intercept exceptions from workflow steps on a more global level. \ No newline at end of file diff --git a/docs/docs/extensions.md b/docs/docs/extensions.md new file mode 100644 index 000000000..e370f2b81 --- /dev/null +++ b/docs/docs/extensions.md @@ -0,0 +1,3 @@ +## Extensions + +* [User (human) workflows](https://github.com/danielgerlag/workflow-core/tree/master/src/extensions/WorkflowCore.Users) \ No newline at end of file diff --git a/docs/docs/external-events.md b/docs/docs/external-events.md new file mode 100644 index 000000000..3ed3b3cf4 --- /dev/null +++ b/docs/docs/external-events.md @@ -0,0 +1,26 @@ +# Events + +A workflow can also wait for an external event before proceeding. In the following example, the workflow will wait for an event called *"MyEvent"* with a key of *0*. Once an external source has fired this event, the workflow will wake up and continue processing, passing the data generated by the event onto the next step. + +```C# +public class EventSampleWorkflow : IWorkflow +{ + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .WaitFor("MyEvent", data => "0") + .Output(data => data.Value, step => step.EventData) + .Then() + .Input(step => step.Message, data => "The data from the event is " + data.Value); + } +} +... +//External events are published via the host +//All workflows that have subscribed to MyEvent 0, will be passed "hello" +host.PublishEvent("MyEvent", "0", "hello"); +``` + +## Effective Date + +You can also specify an effective date when waiting for events, which allows you to respond to events that may have already occurred in the past, or only ones that occur after the effective date. diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md new file mode 100644 index 000000000..48ee3b904 --- /dev/null +++ b/docs/docs/getting-started.md @@ -0,0 +1,270 @@ +# Basic Concepts + +## Steps + +A workflow consists of a series of connected steps. Each step can have inputs and produce outputs that can be passed back to the workflow within which it exists. + +Steps are defined by creating a class that inherits from the `StepBody` or `StepBodyAsync` abstract classes and implementing the Run/RunAsync method. They can also be created inline while defining the workflow structure. + +### First we define some steps + +```C# +public class HelloWorld : StepBody +{ + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Hello world"); + return ExecutionResult.Next(); + } +} +``` +*The `StepBody` and `StepBodyAsync` class implementations are constructed by the workflow host which first tries to use IServiceProvider for dependency injection, if it can't construct it with this method, it will search for a parameterless constructor* + +### Then we define the workflow structure by composing a chain of steps. The is done by implementing the IWorkflow interface + +```C# +public class HelloWorldWorkflow : IWorkflow +{ + public string Id => "HelloWorld"; + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Then(); + } +} +``` +The *IWorkflow* interface also has a readonly Id property and readonly Version property. These are used by the workflow host to identify a workflow definition. + +This workflow implemented in JSON would look like this +```json +{ + "Id": "HelloWorld", + "Version": 1, + "Steps": [ + { + "Id": "Hello", + "StepType": "MyApp.HelloWorld, MyApp", + "NextStepId": "Bye" + }, + { + "Id": "Bye", + "StepType": "MyApp.GoodbyeWorld, MyApp" + } + ] +} +``` + + +### You can also define your steps inline + +```C# +public class HelloWorldWorkflow : IWorkflow +{ + public string Id => "HelloWorld"; + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => + { + Console.WriteLine("Hello world"); + return ExecutionResult.Next(); + }) + .Then(context => + { + Console.WriteLine("Goodbye world"); + return ExecutionResult.Next(); + }); + } +} +``` + +Each running workflow is persisted to the chosen persistence provider between each step, where it can be picked up at a later point in time to continue execution. The outcome result of your step can instruct the workflow host to defer further execution of the workflow until a future point in time or in response to an external event. + +## Host + +The workflow host is the service responsible for executing workflows. It does this by polling the persistence provider for workflow instances that are ready to run, executes them and then passes them back to the persistence provider to by stored for the next time they are run. It is also responsible for publishing events to any workflows that may be waiting on one. + +### Setup + +Use the *AddWorkflow* extension method for *IServiceCollection* to configure the workflow host upon startup of your application. +By default, it is configured with *MemoryPersistenceProvider* and *SingleNodeConcurrencyProvider* for testing purposes. You can also configure a DB persistence provider at this point. + +```C# +services.AddWorkflow(); +``` + +### Usage + +When your application starts, grab the workflow host from the built-in dependency injection framework *IServiceProvider*. Make sure you call *RegisterWorkflow*, so that the workflow host knows about all your workflows, and then call *Start()* to fire up the thread pool that executes workflows. Use the *StartWorkflow* method to initiate a new instance of a particular workflow. + + +```C# +var host = serviceProvider.GetService(); +host.RegisterWorkflow(); +host.Start(); + +host.StartWorkflow("HelloWorld", 1, null); + +Console.ReadLine(); +host.Stop(); +``` + +## Passing data between steps + +Each step is intended to be a black-box, therefore they support inputs and outputs. These inputs and outputs can be mapped to a data class that defines the custom data relevant to each workflow instance. + +The following sample shows how to define inputs and outputs on a step, it then shows how define a workflow with a typed class for internal data and how to map the inputs and outputs to properties on the custom data class. + +```C# +//Our workflow step with inputs and outputs +public class AddNumbers : StepBody +{ + public int Input1 { get; set; } + + public int Input2 { get; set; } + + public int Output { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Output = (Input1 + Input2); + return ExecutionResult.Next(); + } +} + +//Our class to define the internal data of our workflow +public class MyDataClass +{ + public int Value1 { get; set; } + public int Value2 { get; set; } + public int Value3 { get; set; } +} + +//Our workflow definition with strongly typed internal data and mapped inputs & outputs +public class PassingDataWorkflow : IWorkflow +{ + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Input(step => step.Input1, data => data.Value1) + .Input(step => step.Input2, data => data.Value2) + .Output(data => data.Value3, step => step.Output) + .Then() + .Input(step => step.Message, data => "The answer is " + data.Value3.ToString()); + } + ... +} + +``` + +or in jSON format +```json +{ + "Id": "AddWorkflow", + "Version": 1, + "DataType": "MyApp.MyDataClass, MyApp", + "Steps": [ + { + "Id": "Add", + "StepType": "MyApp.AddNumbers, MyApp", + "NextStepId": "ShowResult", + "Inputs": { + "Value1": "data.Value1", + "Value2": "data.Value2" + }, + "Outputs": { + "Answer": "step.Output" + } + }, + { + "Id": "ShowResult", + "StepType": "MyApp.CustomMessage, MyApp", + "Inputs": { + "Message": "\"The answer is \" + data.Value1" + } + } + ] +} +``` + +or in YAML format +```yaml +Id: AddWorkflow +Version: 1 +DataType: MyApp.MyDataClass, MyApp +Steps: +- Id: Add + StepType: MyApp.AddNumbers, MyApp + NextStepId: ShowResult + Inputs: + Value1: data.Value1 + Value2: data.Value2 + Outputs: + Answer: step.Output +- Id: ShowResult + StepType: MyApp.CustomMessage, MyApp + Inputs: + Message: '"The answer is " + data.Value1' +``` + + +## Injecting dependencies into steps + +If you register your step classes with the IoC container, the workflow host will use the IoC container to construct them and therefore inject any required dependencies. This example illustrates the use of dependency injection for workflow steps. + +Consider the following service + +```C# +public interface IMyService +{ + void DoTheThings(); +} +... +public class MyService : IMyService +{ + public void DoTheThings() + { + Console.WriteLine("Doing stuff..."); + } +} +``` + +Which is consumed by a workflow step as follows + +```C# +public class DoSomething : StepBody +{ + private IMyService _myService; + + public DoSomething(IMyService myService) + { + _myService = myService; + } + + public override ExecutionResult Run(IStepExecutionContext context) + { + _myService.DoTheThings(); + return ExecutionResult.Next(); + } +} +``` + +Simply add both the service and the workflow step as transients to the service collection when setting up your IoC container. +(Avoid registering steps as singletons, since multiple concurrent workflows may need to use them at once.) + +```C# +IServiceCollection services = new ServiceCollection(); +services.AddLogging(); +services.AddWorkflow(); + +services.AddTransient(); +services.AddTransient(); +``` + + diff --git a/docs/docs/index.md b/docs/docs/index.md new file mode 100644 index 000000000..ac91c1aad --- /dev/null +++ b/docs/docs/index.md @@ -0,0 +1,34 @@ +# Workflow Core + +Workflow Core is a light weight workflow engine targeting .NET Standard. Think: long running processes with multiple tasks that need to track state. It supports pluggable persistence and concurrency providers to allow for multi-node clusters. + +## Installing + +Install the NuGet package "WorkflowCore" + +Using nuget +``` +PM> Install-Package WorkflowCore +``` + +Using .net cli +``` +dotnet add package WorkflowCore +``` + +## Fluent API + +Define workflows with the fluent API. + +```c# +public class MyWorkflow : IWorkflow +{ + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Then() + .Then; + } +} +``` \ No newline at end of file diff --git a/docs/docs/json-yaml.md b/docs/docs/json-yaml.md new file mode 100644 index 000000000..431384240 --- /dev/null +++ b/docs/docs/json-yaml.md @@ -0,0 +1,444 @@ +# Loading workflow definitions from JSON or YAML + +Simply grab the `DefinitionLoader` from the IoC container and call the `.LoadDefinition` method + +```c# +using using WorkflowCore.Services.DefinitionStorage; +... +var loader = serviceProvider.GetService(); +loader.LoadDefinition("<>", Deserializers.Json); +``` + +## Common DSL + +Both the JSON and YAML formats follow a common DSL, where step types within the workflow are referenced by the fully qualified class names. +Built-in step types typically live in the `WorklfowCore.Primitives` namespace. + +| Field | Description | +| ----------------------- | --------------------------- | +| Id | Workflow Definition ID | +| Version | Workflow Definition Version | +| DataType | Fully qualified assembly class name of the custom data object | +| Steps[].Id | Step ID (required unique key for each step) | +| Steps[].StepType | Fully qualified assembly class name of the step | +| Steps[].NextStepId | Step ID of the next step after this one completes | +| Steps[].Inputs | Optional Key/value pair of step inputs | +| Steps[].Outputs | Optional Key/value pair of step outputs | +| Steps[].CancelCondition | Optional cancel condition | + +```json +{ + "Id": "HelloWorld", + "Version": 1, + "Steps": [ + { + "Id": "Hello", + "StepType": "MyApp.HelloWorld, MyApp", + "NextStepId": "Bye" + }, + { + "Id": "Bye", + "StepType": "MyApp.GoodbyeWorld, MyApp" + } + ] +} +``` +```yaml +Id: HelloWorld +Version: 1 +Steps: +- Id: Hello + StepType: MyApp.HelloWorld, MyApp + NextStepId: Bye +- Id: Bye + StepType: MyApp.GoodbyeWorld, MyApp +``` + +### Inputs and Outputs + +Inputs and outputs can be bound to a step as a key/value pair object, +* The `Inputs` collection, the key would match a property on the `Step` class and the value would be an expression with both the `data` and `context` parameters at your disposal. +* The `Outputs` collection, the key would match a property on the `Data` class and the value would be an expression with both the `step` as a parameter at your disposal. + +Full details of the capabilities of expression language can be found [here](https://github.com/StefH/System.Linq.Dynamic.Core/wiki/Dynamic-Expressions#expression-language) + +```json +{ + "Id": "AddWorkflow", + "Version": 1, + "DataType": "MyApp.MyDataClass, MyApp", + "Steps": [ + { + "Id": "Hello", + "StepType": "MyApp.HelloWorld, MyApp", + "NextStepId": "Add" + }, + { + "Id": "Add", + "StepType": "MyApp.AddNumbers, MyApp", + "NextStepId": "Bye", + "Inputs": { + "Value1": "data.Value1", + "Value2": "data.Value2" + }, + "Outputs": { + "Answer": "step.Result" + } + }, + { + "Id": "Bye", + "StepType": "MyApp.GoodbyeWorld, MyApp" + } + ] +} +``` +```yaml +Id: AddWorkflow +Version: 1 +DataType: MyApp.MyDataClass, MyApp +Steps: +- Id: Hello + StepType: MyApp.HelloWorld, MyApp + NextStepId: Add +- Id: Add + StepType: MyApp.AddNumbers, MyApp + NextStepId: Bye + Inputs: + Value1: data.Value1 + Value2: data.Value2 + Outputs: + Answer: step.Result +- Id: Bye + StepType: MyApp.GoodbyeWorld, MyApp +``` + +```json +{ + "Id": "AddWorkflow", + "Version": 1, + "DataType": "MyApp.MyDataClass, MyApp", + "Steps": [ + { + "Id": "Hello", + "StepType": "MyApp.HelloWorld, MyApp", + "NextStepId": "Print" + }, + { + "Id": "Print", + "StepType": "MyApp.PrintMessage, MyApp", + "Inputs": { "Message": "\"Hi there!\"" } + } + ] +} +``` +```yaml +Id: AddWorkflow +Version: 1 +DataType: MyApp.MyDataClass, MyApp +Steps: +- Id: Hello + StepType: MyApp.HelloWorld, MyApp + NextStepId: Print +- Id: Print + StepType: MyApp.PrintMessage, MyApp + Inputs: + Message: '"Hi there!"' + +``` + +### WaitFor + +The `.WaitFor` can be implemented using 3 inputs as follows + +| Field | Description | +| ---------------------- | --------------------------- | +| CancelCondition | Optional expression to specify a cancel condition | +| Inputs.EventName | Expression to specify the event name | +| Inputs.EventKey | Expression to specify the event key | +| Inputs.EffectiveDate | Optional expression to specify the effective date | + + +```json +{ + "Id": "MyWaitStep", + "StepType": "WorkflowCore.Primitives.WaitFor, WorkflowCore", + "NextStepId": "...", + "CancelCondition": "...", + "Inputs": { + "EventName": "\"Event1\"", + "EventKey": "\"Key1\"", + "EffectiveDate": "DateTime.Now" + } +} +``` +```yaml +Id: MyWaitStep +StepType: WorkflowCore.Primitives.WaitFor, WorkflowCore +NextStepId: "..." +CancelCondition: "..." +Inputs: + EventName: '"Event1"' + EventKey: '"Key1"' + EffectiveDate: DateTime.Now + +``` + +### If + +The `.If` can be implemented as follows + +```json +{ + "Id": "MyIfStep", + "StepType": "WorkflowCore.Primitives.If, WorkflowCore", + "NextStepId": "...", + "Inputs": { "Condition": "<>" }, + "Do": [[ + { + "Id": "do1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "do2" + }, + { + "Id": "do2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ]] +} +``` +```yaml +Id: MyIfStep +StepType: WorkflowCore.Primitives.If, WorkflowCore +NextStepId: "..." +Inputs: + Condition: "<>" +Do: +- - Id: do1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: do2 + - Id: do2 + StepType: MyApp.DoSomething2, MyApp + +``` + +### While + +The `.While` can be implemented as follows + +```json +{ + "Id": "MyWhileStep", + "StepType": "WorkflowCore.Primitives.While, WorkflowCore", + "NextStepId": "...", + "Inputs": { "Condition": "<>" }, + "Do": [[ + { + "Id": "do1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "do2" + }, + { + "Id": "do2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ]] +} +``` +```yaml +Id: MyWhileStep +StepType: WorkflowCore.Primitives.While, WorkflowCore +NextStepId: "..." +Inputs: + Condition: "<>" +Do: +- - Id: do1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: do2 + - Id: do2 + StepType: MyApp.DoSomething2, MyApp + +``` + +### ForEach + +The `.ForEach` can be implemented as follows + +```json +{ + "Id": "MyForEachStep", + "StepType": "WorkflowCore.Primitives.ForEach, WorkflowCore", + "NextStepId": "...", + "Inputs": { "Collection": "<>" }, + "Do": [[ + { + "Id": "do1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "do2" + }, + { + "Id": "do2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ]] +} +``` +```yaml +Id: MyForEachStep +StepType: WorkflowCore.Primitives.ForEach, WorkflowCore +NextStepId: "..." +Inputs: + Collection: "<>" +Do: +- - Id: do1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: do2 + - Id: do2 + StepType: MyApp.DoSomething2, MyApp +``` + +### Delay + +The `.Delay` can be implemented as follows + +```json +{ + "Id": "MyDelayStep", + "StepType": "WorkflowCore.Primitives.Delay, WorkflowCore", + "NextStepId": "...", + "Inputs": { "Period": "<>" } +} +``` +```yaml +Id: MyDelayStep +StepType: WorkflowCore.Primitives.Delay, WorkflowCore +NextStepId: "..." +Inputs: + Period: "<>" +``` + + +### Parallel + +The `.Parallel` can be implemented as follows + +```json +{ + "Id": "MyParallelStep", + "StepType": "WorkflowCore.Primitives.Sequence, WorkflowCore", + "NextStepId": "...", + "Do": [ + [ + { + "Id": "Branch1.Step1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "Branch1.Step2" + }, + { + "Id": "Branch1.Step2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ], + [ + { + "Id": "Branch2.Step1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "Branch2.Step2" + }, + { + "Id": "Branch2.Step2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ] + ] +} +``` +```yaml +Id: MyParallelStep +StepType: WorkflowCore.Primitives.Sequence, WorkflowCore +NextStepId: "..." +Do: +- - Id: Branch1.Step1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: Branch1.Step2 + - Id: Branch1.Step2 + StepType: MyApp.DoSomething2, MyApp +- - Id: Branch2.Step1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: Branch2.Step2 + - Id: Branch2.Step2 + StepType: MyApp.DoSomething2, MyApp +``` + +### Schedule + +The `.Schedule` can be implemented as follows + +```json +{ + "Id": "MyScheduleStep", + "StepType": "WorkflowCore.Primitives.Schedule, WorkflowCore", + "Inputs": { "Interval": "<>" }, + "Do": [[ + { + "Id": "do1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "do2" + }, + { + "Id": "do2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ]] +} +``` +```yaml +Id: MyScheduleStep +StepType: WorkflowCore.Primitives.Schedule, WorkflowCore +Inputs: + Interval: "<>" +Do: +- - Id: do1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: do2 + - Id: do2 + StepType: MyApp.DoSomething2, MyApp +``` + +### Recur + +The `.Recur` can be implemented as follows + +```json +{ + "Id": "MyScheduleStep", + "StepType": "WorkflowCore.Primitives.Recur, WorkflowCore", + "Inputs": { + "Interval": "<>", + "StopCondition": "<>" + }, + "Do": [[ + { + "Id": "do1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "do2" + }, + { + "Id": "do2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ]] +} +``` +```yaml +Id: MyScheduleStep +StepType: WorkflowCore.Primitives.Recur, WorkflowCore +Inputs: + Interval: "<>" + StopCondition: "<>" +Do: +- - Id: do1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: do2 + - Id: do2 + StepType: MyApp.DoSomething2, MyApp +``` diff --git a/docs/docs/multi-node-clusters.md b/docs/docs/multi-node-clusters.md new file mode 100644 index 000000000..c61c87164 --- /dev/null +++ b/docs/docs/multi-node-clusters.md @@ -0,0 +1,20 @@ +# Multi-node clusters + +By default, the WorkflowHost service will run as a single node using the built-in queue and locking providers for a single node configuration. Should you wish to run a multi-node cluster, you will need to configure an external queueing mechanism and a distributed lock manager to co-ordinate the cluster. These are the providers that are currently available. + +## Queue Providers + +* SingleNodeQueueProvider *(Default built-in provider)* +* [Azure Storage Queues](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.Azure) +* [Redis](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.Redis) +* [RabbitMQ](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.QueueProviders.RabbitMQ) +* [AWS Simple Queue Service](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.AWS) + + +## Distributed lock managers + +* SingleNodeLockProvider *(Default built-in provider)* +* [Azure Storage Leases](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.Azure) +* [Redis](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.Redis) +* [AWS DynamoDB](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.AWS) + diff --git a/docs/docs/persistence.md b/docs/docs/persistence.md new file mode 100644 index 000000000..81a880c63 --- /dev/null +++ b/docs/docs/persistence.md @@ -0,0 +1,12 @@ +# Persistence + +Since workflows are typically long running processes, they will need to be persisted to storage between steps. +There are several persistence providers available as separate Nuget packages. + +* MemoryPersistenceProvider *(Default provider, for demo and testing purposes)* +* [MongoDB](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Persistence.MongoDB) +* [SQL Server](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Persistence.SqlServer) +* [PostgreSQL](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Persistence.PostgreSQL) +* [Sqlite](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Persistence.Sqlite) +* [Amazon DynamoDB](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.AWS) +* [Redis](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.Redis) diff --git a/docs/docs/sagas.md b/docs/docs/sagas.md new file mode 100644 index 000000000..11d354657 --- /dev/null +++ b/docs/docs/sagas.md @@ -0,0 +1,161 @@ +# Saga transaction with compensation + +A Saga allows you to encapsulate a sequence of steps within a saga transaction and specify compensation steps for each. + +In the sample, `Task2` will throw an exception, then `UndoTask2` and `UndoTask1` will be triggered. + +```c# +builder + .StartWith(context => Console.WriteLine("Begin")) + .Saga(saga => saga + .StartWith() + .CompensateWith() + .Then() + .CompensateWith() + .Then() + .CompensateWith() + ) + .OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5)) + .Then(context => Console.WriteLine("End")); +``` + +## Retry policy for failed saga transaction + +This particular example will retry the saga every 5 seconds, but you could also simply fail completely, and process a master compensation task for the whole saga. + +```c# +builder + .StartWith(context => Console.WriteLine("Begin")) + .Saga(saga => saga + .StartWith() + .CompensateWith() + .Then() + .CompensateWith() + .Then() + .CompensateWith() + ) + .CompensateWith() + .Then(context => Console.WriteLine("End")); +``` + +## Compensate entire saga transaction + +You could also only specify a master compensation step, as follows + +```c# +builder + .StartWith(context => Console.WriteLine("Begin")) + .Saga(saga => saga + .StartWith() + .Then() + .Then() + ) + .CompensateWith() + .Then(context => Console.WriteLine("End")); +``` + +## Passing parameters to compensation steps + +Parameters can be passed to a compensation step as follows + +```c# +builder + .StartWith() + .CompensateWith(compensate => + { + compensate.Input(step => step.Message, data => "undoing..."); + }) +``` + +## Expressing a saga in JSON or YAML + +A saga transaction can be expressed in JSON or YAML, by using the `WorkflowCore.Primitives.Sequence` step and setting the `Saga` parameter to `true`. + +The compensation steps can be defined by specifying the `CompensateWith` parameter. + +```json +{ + "Id": "Saga-Sample", + "Version": 1, + "DataType": "MyApp.MyDataClass, MyApp", + "Steps": [ + { + "Id": "Hello", + "StepType": "MyApp.HelloWorld, MyApp", + "NextStepId": "MySaga" + }, + { + "Id": "MySaga", + "StepType": "WorkflowCore.Primitives.Sequence, WorkflowCore", + "NextStepId": "Bye", + "Saga": true, + "Do": [ + [ + { + "Id": "do1", + "StepType": "MyApp.Task1, MyApp", + "NextStepId": "do2", + "CompensateWith": [ + { + "Id": "undo1", + "StepType": "MyApp.UndoTask1, MyApp" + } + ] + }, + { + "Id": "do2", + "StepType": "MyApp.Task2, MyApp", + "CompensateWith": [ + { + "Id": "undo2-1", + "NextStepId": "undo2-2", + "StepType": "MyApp.UndoTask2, MyApp" + }, + { + "Id": "undo2-2", + "StepType": "MyApp.DoSomethingElse, MyApp" + } + ] + } + ] + ] + }, + { + "Id": "Bye", + "StepType": "MyApp.GoodbyeWorld, MyApp" + } + ] +} +``` + +```yaml +Id: Saga-Sample +Version: 1 +DataType: MyApp.MyDataClass, MyApp +Steps: +- Id: Hello + StepType: MyApp.HelloWorld, MyApp + NextStepId: MySaga +- Id: MySaga + StepType: WorkflowCore.Primitives.Sequence, WorkflowCore + NextStepId: Bye + Saga: true + Do: + - - Id: do1 + StepType: MyApp.Task1, MyApp + NextStepId: do2 + CompensateWith: + - Id: undo1 + StepType: MyApp.UndoTask1, MyApp + - Id: do2 + StepType: MyApp.Task2, MyApp + CompensateWith: + - Id: undo2-1 + NextStepId: undo2-2 + StepType: MyApp.UndoTask2, MyApp + - Id: undo2-2 + StepType: MyApp.DoSomethingElse, MyApp +- Id: Bye + StepType: MyApp.GoodbyeWorld, MyApp + +``` \ No newline at end of file diff --git a/docs/docs/samples.md b/docs/docs/samples.md new file mode 100644 index 000000000..4afa753d3 --- /dev/null +++ b/docs/docs/samples.md @@ -0,0 +1,33 @@ +# Samples + +[Hello World](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample01) + +[Passing Data](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample03) + +[Events](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample04) + +[Dependency Injection](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample15) + +[Parallel ForEach](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample09) + +[While loop](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample10) + +[If](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample11) + +[Parallel Tasks](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample13) + +[Saga Transactions](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample17) + +[Scheduled Background Tasks](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample16) + +[Recurring Background Tasks](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample14) + +[Multiple outcomes](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample12) + +[Deferred execution & re-entrant steps](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample05) + +[Looping](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample02) + +[Exposing a REST API](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WebApiSample) + +[Human(User) Workflow](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample08) \ No newline at end of file diff --git a/docs/docs/using-with-aspnet-core.md b/docs/docs/using-with-aspnet-core.md new file mode 100644 index 000000000..51b0d54ac --- /dev/null +++ b/docs/docs/using-with-aspnet-core.md @@ -0,0 +1,47 @@ +# Using with ASP.NET Core +## How to configure within an ASP.NET Core application + +In your startup class, use the `AddWorkflow` extension method to configure workflow core services, and then register your workflows and start the host when you configure the app. +```c# +public class Startup +{ + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddMvc(); + services.AddWorkflow(cfg => + { + cfg.UseMongoDB(@"mongodb://mongo:27017", "workflow"); + cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://elastic:9200")), "workflows"); + }); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseMvc(); + + var host = app.ApplicationServices.GetService(); + host.RegisterWorkflow(); + host.Start(); + } +} +``` + +## Usage + +Now simply inject the services you require into your controllers +* IWorkflowController +* IWorkflowHost +* ISearchIndex +* IPersistenceProvider \ No newline at end of file diff --git a/docs/docs/wip/multiple-outcomes.md b/docs/docs/wip/multiple-outcomes.md new file mode 100644 index 000000000..b8293f783 --- /dev/null +++ b/docs/docs/wip/multiple-outcomes.md @@ -0,0 +1,21 @@ +### Multiple outcomes / forking + +A workflow can take a different path depending on the outcomes of preceeding steps. The following example shows a process where first a random number of 0 or 1 is generated and is the outcome of the first step. Then, depending on the outcome value, the workflow will either fork to (TaskA + TaskB) or (TaskC + TaskD) + +```C# +public class MultipleOutcomeWorkflow : IWorkflow +{ + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(x => x.Name("Random Step")) + .When(data => 0).Do(then => then + .StartWith() + .Then()) + .When(data => 1).Do(then => then + .StartWith() + .Then()) + .Then(); + } +} +``` \ No newline at end of file diff --git a/docs/docs/wip/steps-deep.md b/docs/docs/wip/steps-deep.md new file mode 100644 index 000000000..a926eb925 --- /dev/null +++ b/docs/docs/wip/steps-deep.md @@ -0,0 +1,16 @@ +The first time a particular step within the workflow is called, the PersistenceData property on the context object is *null*. The ExecutionResult produced by the Run method can either cause the workflow to proceed to the next step by providing an outcome value, instruct the workflow to sleep for a defined period or simply not move the workflow forward. If no outcome value is produced, then the step becomes re-entrant by setting PersistenceData, so the workflow host will call this step again in the future buy will populate the PersistenceData with it's previous value. + +For example, this step will initially run with *null* PersistenceData and put the workflow to sleep for 12 hours, while setting the PersistenceData to *new Object()*. 12 hours later, the step will be called again but context.PersistenceData will now contain the object constructed in the previous iteration, and will now produce an outcome value of *null*, causing the workflow to move forward. + +```C# +public class SleepStep : StepBody +{ + public override ExecutionResult Run(IStepExecutionContext context) + { + if (context.PersistenceData == null) + return ExecutionResult.Sleep(Timespan.FromHours(12), new Object()); + else + return ExecutionResult.Next(); + } +} +``` diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 000000000..7cd67a67d --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,15 @@ +site_name: Workflow Core +nav: + - Home: index.md + - Getting started: getting-started.md + - External events: external-events.md + - Error handing: error-handling.md + - Control structures: control-structures.md + - Saga transactions: sagas.md + - JSON / YAML Definitions: json-yaml.md + - Persistence: persistence.md + - Multi-node clusters: multi-node-clusters.md + - ASP.NET Core.md: using-with-aspnet-core.md + - Extensions: extensions.md + - Samples: samples.md +theme: readthedocs \ No newline at end of file From 3c48a32fafedcab3475a6c5037828b3f3a27c909 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 28 Jul 2019 08:31:00 -0700 Subject: [PATCH 144/462] docs --- docs/{docs => }/control-structures.md | 0 docs/{docs => }/error-handling.md | 0 docs/{docs => }/extensions.md | 0 docs/{docs => }/external-events.md | 0 docs/{docs => }/getting-started.md | 0 docs/{docs => }/index.md | 0 docs/{docs => }/json-yaml.md | 0 docs/{docs => }/multi-node-clusters.md | 0 docs/{docs => }/persistence.md | 0 docs/{docs => }/sagas.md | 0 docs/{docs => }/samples.md | 0 docs/{docs => }/using-with-aspnet-core.md | 0 docs/{docs => }/wip/multiple-outcomes.md | 0 docs/{docs => }/wip/steps-deep.md | 0 docs/mkdocs.yml => mkdocs.yml | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename docs/{docs => }/control-structures.md (100%) rename docs/{docs => }/error-handling.md (100%) rename docs/{docs => }/extensions.md (100%) rename docs/{docs => }/external-events.md (100%) rename docs/{docs => }/getting-started.md (100%) rename docs/{docs => }/index.md (100%) rename docs/{docs => }/json-yaml.md (100%) rename docs/{docs => }/multi-node-clusters.md (100%) rename docs/{docs => }/persistence.md (100%) rename docs/{docs => }/sagas.md (100%) rename docs/{docs => }/samples.md (100%) rename docs/{docs => }/using-with-aspnet-core.md (100%) rename docs/{docs => }/wip/multiple-outcomes.md (100%) rename docs/{docs => }/wip/steps-deep.md (100%) rename docs/mkdocs.yml => mkdocs.yml (100%) diff --git a/docs/docs/control-structures.md b/docs/control-structures.md similarity index 100% rename from docs/docs/control-structures.md rename to docs/control-structures.md diff --git a/docs/docs/error-handling.md b/docs/error-handling.md similarity index 100% rename from docs/docs/error-handling.md rename to docs/error-handling.md diff --git a/docs/docs/extensions.md b/docs/extensions.md similarity index 100% rename from docs/docs/extensions.md rename to docs/extensions.md diff --git a/docs/docs/external-events.md b/docs/external-events.md similarity index 100% rename from docs/docs/external-events.md rename to docs/external-events.md diff --git a/docs/docs/getting-started.md b/docs/getting-started.md similarity index 100% rename from docs/docs/getting-started.md rename to docs/getting-started.md diff --git a/docs/docs/index.md b/docs/index.md similarity index 100% rename from docs/docs/index.md rename to docs/index.md diff --git a/docs/docs/json-yaml.md b/docs/json-yaml.md similarity index 100% rename from docs/docs/json-yaml.md rename to docs/json-yaml.md diff --git a/docs/docs/multi-node-clusters.md b/docs/multi-node-clusters.md similarity index 100% rename from docs/docs/multi-node-clusters.md rename to docs/multi-node-clusters.md diff --git a/docs/docs/persistence.md b/docs/persistence.md similarity index 100% rename from docs/docs/persistence.md rename to docs/persistence.md diff --git a/docs/docs/sagas.md b/docs/sagas.md similarity index 100% rename from docs/docs/sagas.md rename to docs/sagas.md diff --git a/docs/docs/samples.md b/docs/samples.md similarity index 100% rename from docs/docs/samples.md rename to docs/samples.md diff --git a/docs/docs/using-with-aspnet-core.md b/docs/using-with-aspnet-core.md similarity index 100% rename from docs/docs/using-with-aspnet-core.md rename to docs/using-with-aspnet-core.md diff --git a/docs/docs/wip/multiple-outcomes.md b/docs/wip/multiple-outcomes.md similarity index 100% rename from docs/docs/wip/multiple-outcomes.md rename to docs/wip/multiple-outcomes.md diff --git a/docs/docs/wip/steps-deep.md b/docs/wip/steps-deep.md similarity index 100% rename from docs/docs/wip/steps-deep.md rename to docs/wip/steps-deep.md diff --git a/docs/mkdocs.yml b/mkdocs.yml similarity index 100% rename from docs/mkdocs.yml rename to mkdocs.yml From 3dfab8ae2854c463ca1fbff082f96bcf7ece0246 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 28 Jul 2019 08:34:02 -0700 Subject: [PATCH 145/462] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f750eff44..64ab6068d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ https://github.com/danielgerlag/conductor ## Documentation -See [Tutorial here.](https://github.com/danielgerlag/workflow-core/wiki) +See [Tutorial here.](https://workflow-core.readthedocs.io) ## Fluent API From 5e7f31e979276c98af9e37bf5866dae0862b580d Mon Sep 17 00:00:00 2001 From: Poorya Date: Mon, 5 Aug 2019 12:51:10 +0200 Subject: [PATCH 146/462] update version --- .../WorkflowCore.Persistence.PostgreSQL.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index e210a8b55..484dc6941 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,9 +15,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 2.0.0 - 2.0.0.0 - 2.0.0.0 + 2.0.1 + 2.0.1.0 + 2.0.1.0 From db43cc15805f7a2d5702829a55d7b091f051f772 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 11 Aug 2019 13:04:35 -0700 Subject: [PATCH 147/462] Update WorkflowCore.Persistence.PostgreSQL.csproj --- .../WorkflowCore.Persistence.PostgreSQL.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 484dc6941..a7dcd8f60 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,9 +15,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 2.0.1 - 2.0.1.0 - 2.0.1.0 + 2.0.2 + 2.0.2.0 + 2.0.2.0 From 97e5e2fcec5ee092710f2d1c26ed6e6da44c1566 Mon Sep 17 00:00:00 2001 From: Tailslide <5757188+Tailslide@users.noreply.github.com> Date: Fri, 30 Aug 2019 09:33:56 -0600 Subject: [PATCH 148/462] Fix bug where workflow timer kept running after workflow completed. Fix bug where no approval chose yes instead. --- src/samples/WorkflowCore.Sample08/Program.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/samples/WorkflowCore.Sample08/Program.cs b/src/samples/WorkflowCore.Sample08/Program.cs index 1c4ba2036..0c3302638 100644 --- a/src/samples/WorkflowCore.Sample08/Program.cs +++ b/src/samples/WorkflowCore.Sample08/Program.cs @@ -40,14 +40,18 @@ public static void Main(string[] args) } //Thread.Sleep(500); - - Console.ReadLine(); + + var input = Console.ReadLine(); Console.WriteLine(); - Console.WriteLine("Choosing " + item.Options.First().Key); + var optionselected = item.Options.Single(x => x.Value == input); + Console.WriteLine("Choosing " + optionselected.Key); - host.PublishUserAction(openItems.First().Key, @"domain\john", item.Options.First().Value).Wait(); + host.PublishUserAction(optionselected.Key, @"domain\john", optionselected.Value).Wait(); } + timer.Dispose(); + timer = null; + Console.WriteLine("Workflow ended."); Console.ReadLine(); host.Stop(); } @@ -85,6 +89,6 @@ private static IServiceProvider ConfigureServices() return serviceProvider; } - + } } From 0931aab4ba6756fe6b883f9511175a476e41b9b0 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 30 Aug 2019 09:42:28 -0700 Subject: [PATCH 149/462] requeue active tasks --- src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs | 2 ++ src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 2ff9c275d..87bc068dc 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -77,6 +77,8 @@ private async void Execute() if (activeTasks.ContainsKey(item)) { secondPasses.Add(item); + if (!EnableSecondPasses) + await QueueProvider.QueueWork(item, Queue); continue; } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index c9f2258b4..d08b1d3a3 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 2.0.0 - 2.0.0.0 - 2.0.0.0 + 2.0.1 + 2.0.1.0 + 2.0.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From caeaa4de92c76fe785fa5752e213e58e191205ac Mon Sep 17 00:00:00 2001 From: Alexey Malinin Date: Wed, 4 Sep 2019 22:22:37 +0700 Subject: [PATCH 150/462] Inherit IWorkflowHost from IHostedService --- src/WorkflowCore/Interface/IWorkflowHost.cs | 4 ++-- src/WorkflowCore/Services/WorkflowHost.cs | 26 ++++++++++++++------- src/WorkflowCore/WorkflowCore.csproj | 1 + 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/WorkflowCore/Interface/IWorkflowHost.cs b/src/WorkflowCore/Interface/IWorkflowHost.cs index 6bfc377db..0786b3b78 100644 --- a/src/WorkflowCore/Interface/IWorkflowHost.cs +++ b/src/WorkflowCore/Interface/IWorkflowHost.cs @@ -1,12 +1,12 @@ using Microsoft.Extensions.Logging; using System; -using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; using WorkflowCore.Models; using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Interface { - public interface IWorkflowHost : IWorkflowController + public interface IWorkflowHost : IWorkflowController, IHostedService { /// /// Start the workflow host, this enable execution of workflows diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index 556b982b0..e1d72c1ed 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -78,13 +78,18 @@ public Task PublishEvent(string eventName, string eventKey, object eventData, Da } public void Start() + { + StartAsync(CancellationToken.None).Wait(); + } + + public async Task StartAsync(CancellationToken cancellationToken) { _shutdown = false; PersistenceStore.EnsureStoreExists(); - QueueProvider.Start().Wait(); - LockProvider.Start().Wait(); - _lifeCycleEventHub.Start().Wait(); - _searchIndex.Start().Wait(); + await QueueProvider.Start(); + await LockProvider.Start(); + await _lifeCycleEventHub.Start(); + await _searchIndex.Start(); Logger.LogInformation("Starting background tasks"); @@ -93,6 +98,11 @@ public void Start() } public void Stop() + { + StopAsync(CancellationToken.None).Wait(); + } + + public async Task StopAsync(CancellationToken cancellationToken) { _shutdown = true; @@ -102,10 +112,10 @@ public void Stop() Logger.LogInformation("Worker tasks stopped"); - QueueProvider.Stop().Wait(); - LockProvider.Stop().Wait(); - _searchIndex.Stop().Wait(); - _lifeCycleEventHub.Stop().Wait(); + await QueueProvider.Stop(); + await LockProvider.Stop(); + await _searchIndex.Stop(); + await _lifeCycleEventHub.Stop(); } public void RegisterWorkflow() diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index d08b1d3a3..d53b89922 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -24,6 +24,7 @@ + From 3e0afe3355769b8f33e8cf1510467a13641f0cfa Mon Sep 17 00:00:00 2001 From: Tailslide <5757188+Tailslide@users.noreply.github.com> Date: Wed, 4 Sep 2019 11:14:20 -0600 Subject: [PATCH 151/462] Typo.. passed wrong key so workflow never finished. --- src/samples/WorkflowCore.Sample08/Program.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/samples/WorkflowCore.Sample08/Program.cs b/src/samples/WorkflowCore.Sample08/Program.cs index 0c3302638..c3935cfc8 100644 --- a/src/samples/WorkflowCore.Sample08/Program.cs +++ b/src/samples/WorkflowCore.Sample08/Program.cs @@ -43,12 +43,16 @@ public static void Main(string[] args) var input = Console.ReadLine(); Console.WriteLine(); - var optionselected = item.Options.Single(x => x.Value == input); - Console.WriteLine("Choosing " + optionselected.Key); - host.PublishUserAction(optionselected.Key, @"domain\john", optionselected.Value).Wait(); - } + string key = item.Key; + string value = item.Options.Single(x => x.Value == input).Value; + + Console.WriteLine("Choosing key:" + key + " value:" + value); + host.PublishUserAction(key, @"domain\john", value).Wait(); + } + Thread.Sleep(1000); + Console.WriteLine("Open user actions left:" + host.GetOpenUserActions(workflowId).Count().ToString()); timer.Dispose(); timer = null; Console.WriteLine("Workflow ended."); From 56fb846c1c2c6a3d879a7ec2448b7c1949f0eb0f Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 7 Sep 2019 17:30:26 -0700 Subject: [PATCH 152/462] docs --- docs/elastic-search.md | 132 +++++++++++++++++++++++++++++++++++++++++ docs/test-helpers.md | 83 ++++++++++++++++++++++++++ mkdocs.yml | 4 +- 3 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 docs/elastic-search.md create mode 100644 docs/test-helpers.md diff --git a/docs/elastic-search.md b/docs/elastic-search.md new file mode 100644 index 000000000..ce187de80 --- /dev/null +++ b/docs/elastic-search.md @@ -0,0 +1,132 @@ +# Elasticsearch plugin for Workflow Core + +A search index plugin for Workflow Core backed by Elasticsearch, enabling you to index your workflows and search against the data and state of them. + +## Installing + +Install the NuGet package "WorkflowCore.Providers.Elasticsearch" + +Using Nuget package console +``` +PM> Install-Package WorkflowCore.Providers.Elasticsearch +``` + +Using .NET CLI +``` +dotnet add package WorkflowCore.Providers.Elasticsearch +``` + + +## Configuration + +Use the `.UseElasticsearch` extension method on `IServiceCollection` when building your service provider + +```C# +using Nest; +... +services.AddWorkflow(cfg => +{ + ... + cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://localhost:9200")), "index_name"); +}); +``` + +## Usage + +Inject the `ISearchIndex` service into your code and use the `Search` method. + +``` +Search(string terms, int skip, int take, params SearchFilter[] filters) +``` + +#### terms + +A whitespace separated string of search terms, an empty string will match everything. +This will do a full text search on the following default fields + * Reference + * Description + * Status + * Workflow Definition + + In addition you can search data within your own custom data object if it implements `ISearchable` + + ```c# + using WorkflowCore.Interfaces; + ... + public class MyData : ISearchable +{ + public string StrValue1 { get; set; } + public string StrValue2 { get; set; } + + public IEnumerable GetSearchTokens() + { + return new List() + { + StrValue1, + StrValue2 + }; + } +} + ``` + + ##### Examples + + Search all fields for "puppies" + ```c# + searchIndex.Search("puppies", 0, 10); + ``` + +#### skip & take + +Use `skip` and `take` to page your search results. Where `skip` is the result number to start from and `take` is the page size. + +#### filters + +You can also supply a list of filters to apply to the search, these can be applied to both the standard fields as well as any field within your custom data objects. +There is no need to implement `ISearchable` on your data object in order to use filters against it. + +The following filter types are available + * ScalarFilter + * DateRangeFilter + * NumericRangeFilter + * StatusFilter + + These exist in the `WorkflowCore.Models.Search` namespace. + + ##### Examples + + Filtering by reference + ```c# + using WorkflowCore.Models.Search; + ... + + searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Reference, "My Reference")); + ``` + + Filtering by workflows started after a date + ```c# + searchIndex.Search("", 0, 10, DateRangeFilter.After(x => x.CreateTime, startDate)); + ``` + + Filtering by workflows completed within a period + ```c# + searchIndex.Search("", 0, 10, DateRangeFilter.Between(x => x.CompleteTime, startDate, endDate)); + ``` + + Filtering by workflows in a state + ```c# + searchIndex.Search("", 0, 10, StatusFilter.Equals(WorkflowStatus.Complete)); + ``` + + Filtering against your own custom data class + ```c# + + class MyData + { + public string Value1 { get; set; } + public int Value2 { get; set; } + } + + searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Value1, "blue moon")); + searchIndex.Search("", 0, 10, NumericRangeFilter.LessThan(x => x.Value2, 5)) + ``` diff --git a/docs/test-helpers.md b/docs/test-helpers.md new file mode 100644 index 000000000..9a448a551 --- /dev/null +++ b/docs/test-helpers.md @@ -0,0 +1,83 @@ +# Test helpers for Workflow Core + +Provides support writing tests for workflows built on WorkflowCore + +## Installing + +Install the NuGet package "WorkflowCore.Testing" + +``` +PM> Install-Package WorkflowCore.Testing +``` + +## Usage + +### With xUnit + +* Create a class that inherits from WorkflowTest +* Call the Setup() method in the constructor +* Implement your tests using the helper methods + * StartWorkflow() + * WaitForWorkflowToComplete() + * WaitForEventSubscription() + * GetStatus() + * GetData() + * UnhandledStepErrors + +```C# +public class xUnitTest : WorkflowTest +{ + public xUnitTest() + { + Setup(); + } + + [Fact] + public void MyWorkflow() + { + var workflowId = StartWorkflow(new MyDataClass() { Value1 = 2, Value2 = 3 }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + GetData(workflowId).Value3.Should().Be(5); + } +} +``` + + +### With NUnit + +* Create a class that inherits from WorkflowTest and decorate it with the *TestFixture* attribute +* Override the Setup method and decorate it with the *SetUp* attribute +* Implement your tests using the helper methods + * StartWorkflow() + * WaitForWorkflowToComplete() + * WaitForEventSubscription() + * GetStatus() + * GetData() + * UnhandledStepErrors + +```C# +[TestFixture] +public class NUnitTest : WorkflowTest +{ + [SetUp] + protected override void Setup() + { + base.Setup(); + } + + [Test] + public void NUnit_workflow_test_sample() + { + var workflowId = StartWorkflow(new MyDataClass() { Value1 = 2, Value2 = 3 }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + GetData(workflowId).Value3.Should().Be(5); + } + +} +``` diff --git a/mkdocs.yml b/mkdocs.yml index 7cd67a67d..1e2469827 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,7 +9,9 @@ nav: - JSON / YAML Definitions: json-yaml.md - Persistence: persistence.md - Multi-node clusters: multi-node-clusters.md - - ASP.NET Core.md: using-with-aspnet-core.md + - ASP.NET Core: using-with-aspnet-core.md + - Elasticsearch plugin: elastic-search.md + - Test helpers: test-helpers.md - Extensions: extensions.md - Samples: samples.md theme: readthedocs \ No newline at end of file From 89163d229f27059c7a50adc898031c6e48b66646 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 8 Sep 2019 08:43:25 -0700 Subject: [PATCH 153/462] Sync execution of workflows (#381) --- .../Interface/ISyncWorkflowRunner.cs | 12 +++ .../ServiceCollectionExtensions.cs | 1 + .../Services/SyncWorkflowRunner.cs | 102 ++++++++++++++++++ src/WorkflowCore/WorkflowCore.csproj | 2 +- 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/WorkflowCore/Interface/ISyncWorkflowRunner.cs create mode 100644 src/WorkflowCore/Services/SyncWorkflowRunner.cs diff --git a/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs b/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs new file mode 100644 index 000000000..2fe69771b --- /dev/null +++ b/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface ISyncWorkflowRunner + { + Task RunWorkflowSync(string workflowId, int version, TData data, string reference, TimeSpan timeOut, bool persistSate = true) + where TData : new(); + } +} \ No newline at end of file diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index e6125d2c6..75e40299b 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -59,6 +59,7 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddTransient, InjectedObjectPoolPolicy>(); services.AddTransient, InjectedObjectPoolPolicy>(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/WorkflowCore/Services/SyncWorkflowRunner.cs b/src/WorkflowCore/Services/SyncWorkflowRunner.cs new file mode 100644 index 000000000..ab1b84469 --- /dev/null +++ b/src/WorkflowCore/Services/SyncWorkflowRunner.cs @@ -0,0 +1,102 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using WorkflowCore.Exceptions; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Services +{ + public class SyncWorkflowRunner : ISyncWorkflowRunner + { + private readonly IWorkflowHost _host; + private readonly IWorkflowExecutor _executor; + private readonly IDistributedLockProvider _lockService; + private readonly IWorkflowRegistry _registry; + private readonly IPersistenceProvider _persistenceStore; + private readonly IExecutionPointerFactory _pointerFactory; + private readonly IQueueProvider _queueService; + + public SyncWorkflowRunner(IWorkflowHost host, IWorkflowExecutor executor, IDistributedLockProvider lockService, IWorkflowRegistry registry, IPersistenceProvider persistenceStore, IExecutionPointerFactory pointerFactory, IQueueProvider queueService) + { + _host = host; + _executor = executor; + _lockService = lockService; + _registry = registry; + _persistenceStore = persistenceStore; + _pointerFactory = pointerFactory; + _queueService = queueService; + } + + public async Task RunWorkflowSync(string workflowId, int version, TData data, string reference, TimeSpan timeOut, bool persistSate = true) + where TData : new() + { + var def = _registry.GetDefinition(workflowId, version); + if (def == null) + { + throw new WorkflowNotRegisteredException(workflowId, version); + } + + var wf = new WorkflowInstance + { + WorkflowDefinitionId = workflowId, + Version = def.Version, + Data = data, + Description = def.Description, + NextExecution = 0, + CreateTime = DateTime.Now.ToUniversalTime(), + Status = WorkflowStatus.Suspended, + Reference = reference + }; + + if ((def.DataType != null) && (data == null)) + { + if (typeof(TData) == def.DataType) + wf.Data = new TData(); + else + wf.Data = def.DataType.GetConstructor(new Type[0]).Invoke(new object[0]); + } + + wf.ExecutionPointers.Add(_pointerFactory.BuildGenesisPointer(def)); + + var stopWatch = new Stopwatch(); + + var id = Guid.NewGuid().ToString(); + + if (persistSate) + id = await _persistenceStore.CreateNewWorkflow(wf); + else + wf.Id = id; + + wf.Status = WorkflowStatus.Runnable; + + if (!await _lockService.AcquireLock(id, CancellationToken.None)) + { + throw new InvalidOperationException(); + } + + try + { + stopWatch.Start(); + while ((wf.Status == WorkflowStatus.Runnable) && (timeOut.TotalMilliseconds > stopWatch.ElapsedMilliseconds)) + { + await _executor.Execute(wf); + if (persistSate) + await _persistenceStore.PersistWorkflow(wf); + } + } + finally + { + stopWatch.Stop(); + await _lockService.ReleaseLock(id); + } + + if (persistSate) + await _queueService.QueueWork(id, QueueType.Index); + + return wf; + } + } +} \ No newline at end of file diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index d53b89922..00975ab93 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,7 +15,7 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 2.0.1 + 2.1.0 2.0.1.0 2.0.1.0 From 4e772e48d3c4174dde9b8fc5eec489eee076d050 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 15 Sep 2019 12:00:43 -0700 Subject: [PATCH 154/462] release notes --- ReleaseNotes/2.1.0.md | 10 ++++++++++ WorkflowCore.sln | 1 + 2 files changed, 11 insertions(+) create mode 100644 ReleaseNotes/2.1.0.md diff --git a/ReleaseNotes/2.1.0.md b/ReleaseNotes/2.1.0.md new file mode 100644 index 000000000..69501a2d5 --- /dev/null +++ b/ReleaseNotes/2.1.0.md @@ -0,0 +1,10 @@ +# Workflow Core 2.1.0 + +* Adds the `SyncWorkflowRunner` service that enables workflows to be executed synchronously, you can also avoid persisting the state to the persistence store entirely + +usage +```c# +var runner = serviceProvider.GetService(); +... +var worfklow = await runner.RunWorkflowSync("my-workflow", 1, data, TimeSpan.FromSeconds(10)); +``` \ No newline at end of file diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 2caad68c2..b6f034222 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -99,6 +99,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.9.2.md = ReleaseNotes\1.9.2.md ReleaseNotes\1.9.3.md = ReleaseNotes\1.9.3.md ReleaseNotes\2.0.0.md = ReleaseNotes\2.0.0.md + ReleaseNotes\2.1.0.md = ReleaseNotes\2.1.0.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" From 79bc406d8c436753dcb2c2b90b25d81021989940 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 19 Sep 2019 19:54:25 -0700 Subject: [PATCH 155/462] surface context parameter in Input overload --- src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs | 6 ++++++ src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index d65262ac9..1cf005620 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -118,6 +118,12 @@ public IStepBuilder Input(Action action) return this; } + public IStepBuilder Input(Action action) + { + Step.Inputs.Add(new ActionParameter(action)); + return this; + } + public IStepBuilder Output(Expression> dataProperty, Expression> value) { Step.Outputs.Add(new MemberMapParameter(value, dataProperty)); diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 00975ab93..003a24f03 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 2.1.0 - 2.0.1.0 - 2.0.1.0 + 2.1.1 + 2.1.1.0 + 2.1.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png From 1b49ec147b565c0592c6fe66a61b1bb8d94b82d4 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 19 Sep 2019 20:41:58 -0700 Subject: [PATCH 156/462] interface --- src/WorkflowCore/Interface/IStepBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WorkflowCore/Interface/IStepBuilder.cs b/src/WorkflowCore/Interface/IStepBuilder.cs index 174956a1d..e9bf65817 100644 --- a/src/WorkflowCore/Interface/IStepBuilder.cs +++ b/src/WorkflowCore/Interface/IStepBuilder.cs @@ -97,6 +97,7 @@ public interface IStepBuilder /// /// IStepBuilder Input(Action action); + IStepBuilder Input(Action action); /// /// Map properties on the workflow data object to properties on the step after the step executes From 730cdff5a54418adce98a66a554adaece865d03b Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 24 Sep 2019 10:06:25 +0200 Subject: [PATCH 157/462] Add Azure ServiceBus provider --- .../ServiceCollectionExtensions.cs | 18 ++- .../Services/ServiceBusLifeCycleEventHub.cs | 124 ++++++++++++++++++ .../WorkflowCore.Providers.Azure.csproj | 1 + 3 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 src/providers/WorkflowCore.Providers.Azure/Services/ServiceBusLifeCycleEventHub.cs diff --git a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs index 95ae04031..a9152b7b4 100644 --- a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using WorkflowCore.Models; using WorkflowCore.Providers.Azure.Services; @@ -16,5 +12,17 @@ public static WorkflowOptions UseAzureSyncronization(this WorkflowOptions option options.UseDistributedLockManager(sp => new AzureLockManager(connectionString, sp.GetService())); return options; } + + public static WorkflowOptions UseAzureServiceBus( + this WorkflowOptions options, + string connectionString, + string topicName, + string subscriptionName) + { + options.UseEventHub(sp => new ServiceBusLifeCycleEventHub( + connectionString, topicName, subscriptionName, sp.GetService())); + + return options; + } } } diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/ServiceBusLifeCycleEventHub.cs b/src/providers/WorkflowCore.Providers.Azure/Services/ServiceBusLifeCycleEventHub.cs new file mode 100644 index 000000000..13ba2df55 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Services/ServiceBusLifeCycleEventHub.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Azure.ServiceBus; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using WorkflowCore.Interface; +using WorkflowCore.Models.LifeCycleEvents; + +namespace WorkflowCore.Providers.Azure.Services +{ + public class ServiceBusLifeCycleEventHub : ILifeCycleEventHub + { + private readonly ITopicClient _topicClient; + private readonly ILogger _logger; + private readonly ISubscriptionClient _subscriptionClient; + private readonly ICollection> _subscribers = + new HashSet>(); + private readonly JsonSerializerSettings _serializerSettings = + new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All, + ReferenceLoopHandling = ReferenceLoopHandling.Error, + }; + + public ServiceBusLifeCycleEventHub( + string connectionString, + string topicName, + string subscriptionName, + ILoggerFactory logFactory) + { + _subscriptionClient = new SubscriptionClient( + connectionString, topicName, subscriptionName); + _topicClient = new TopicClient(connectionString, topicName); + _logger = logFactory.CreateLogger(GetType()); + } + + public async Task PublishNotification(LifeCycleEvent evt) + { + var payload = JsonConvert.SerializeObject(evt, _serializerSettings); + var message = new Message(Encoding.Default.GetBytes(payload)) + { + Label = evt.Reference + }; + + await _topicClient.SendAsync(message); + } + + public void Subscribe(Action action) + { + _subscribers.Add(action); + } + + public Task Start() + { + var sessionHandlerOptions = new SessionHandlerOptions(ExceptionHandler) + { + MaxConcurrentSessions = 1, + AutoComplete = false + }; + + _subscriptionClient.RegisterSessionHandler( + MessageHandler, sessionHandlerOptions); + + return Task.CompletedTask; + } + + public async Task Stop() + { + await _topicClient.CloseAsync(); + await _subscriptionClient.CloseAsync(); + } + + private async Task MessageHandler( + IMessageSession messageSession, + Message message, + CancellationToken cancellationToken) + { + try + { + var payload = Encoding.Default.GetString(message.Body); + var evt = JsonConvert.DeserializeObject( + payload, _serializerSettings); + + NotifySubscribers(evt); + + await _subscriptionClient + .CompleteAsync(message.SystemProperties.LockToken) + .ConfigureAwait(false); + } + catch + { + await _subscriptionClient + .AbandonAsync(message.SystemProperties.LockToken); + } + } + + private Task ExceptionHandler(ExceptionReceivedEventArgs arg) + { + _logger.LogWarning( + default, arg.Exception, "Error on receiving events"); + + return Task.CompletedTask; + } + + private void NotifySubscribers(LifeCycleEvent evt) + { + foreach (var subscriber in _subscribers) + { + try + { + subscriber(evt); + } + catch (Exception ex) + { + _logger.LogWarning( + default, ex, $"Error on event subscriber: {ex.Message}"); + } + } + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj index 8b67cbfc9..28a8e06f4 100644 --- a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj +++ b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj @@ -19,6 +19,7 @@ + From a3b93c3336bc75ac1c0ffe8d247bdfa816099393 Mon Sep 17 00:00:00 2001 From: glucaci Date: Wed, 25 Sep 2019 14:05:00 +0200 Subject: [PATCH 158/462] Update readme --- .../WorkflowCore.Providers.Azure/README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.Azure/README.md b/src/providers/WorkflowCore.Providers.Azure/README.md index e27e2df41..858bab77a 100644 --- a/src/providers/WorkflowCore.Providers.Azure/README.md +++ b/src/providers/WorkflowCore.Providers.Azure/README.md @@ -2,6 +2,7 @@ * Provides [DLM](https://en.wikipedia.org/wiki/Distributed_lock_manager) support on [Workflow Core](../../README.md) using Azure Blob Storage leases. * Provides Queueing support on [Workflow Core](../../README.md) using Azure Storage queues. +* Provides event hub support on [Workflow Core](../../README.md) backed by Azure Service Bus. This makes it possible to have a cluster of nodes processing your workflows. @@ -9,14 +10,25 @@ This makes it possible to have a cluster of nodes processing your workflows. Install the NuGet package "WorkflowCore.Providers.Azure" +Using Nuget package console ``` PM> Install-Package WorkflowCore.Providers.Azure ``` +Using .NET CLI +``` +dotnet add package WorkflowCore.Providers.Azure +``` ## Usage -Use the .UseAzureSyncronization extension method when building your service provider. +Use the `IServiceCollection` extension methods when building your service provider +* .UseAzureSyncronization +* .UseAzureServiceBus ```C# -services.AddWorkflow(x => x.UseAzureSyncronization("azure storage connection string")); +services.AddWorkflow(options => +{ + options.UseAzureSyncronization("azure storage connection string"); + options.UseAzureServiceBus("service bus connection string", "topic name", "subscription name"); +}); ``` \ No newline at end of file From 3efd0853f9cb9ca88d9a804c32a4a685abeb61b1 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 6 Oct 2019 16:47:03 -0700 Subject: [PATCH 159/462] Workflow purger (#402) --- ReleaseNotes/2.1.2.md | 9 ++++ WorkflowCore.sln | 1 + src/WorkflowCore/Interface/IWorkflowPurger.cs | 11 ++++ src/WorkflowCore/WorkflowCore.csproj | 6 +-- .../Services/WorkflowPurger.cs | 52 +++++++++++++++++++ ...lowCore.Persistence.EntityFramework.csproj | 6 +-- .../ServiceCollectionExtensions.cs | 7 +++ .../Services/MongoPersistenceProvider.cs | 3 +- .../Services/WorkflowPurger.cs | 26 ++++++++++ .../WorkflowCore.Persistence.MongoDB.csproj | 6 +-- .../ServiceCollectionExtensions.cs | 2 + .../WorkflowCore.Persistence.MySQL.csproj | 6 +-- .../ServiceCollectionExtensions.cs | 2 + ...WorkflowCore.Persistence.PostgreSQL.csproj | 6 +-- .../ServiceCollectionExtensions.cs | 2 + .../WorkflowCore.Persistence.SqlServer.csproj | 6 +-- .../ServiceCollectionExtensions.cs | 2 + .../WorkflowCore.Persistence.Sqlite.csproj | 6 +-- 18 files changed, 137 insertions(+), 22 deletions(-) create mode 100644 ReleaseNotes/2.1.2.md create mode 100644 src/WorkflowCore/Interface/IWorkflowPurger.cs create mode 100644 src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs create mode 100644 src/providers/WorkflowCore.Persistence.MongoDB/Services/WorkflowPurger.cs diff --git a/ReleaseNotes/2.1.2.md b/ReleaseNotes/2.1.2.md new file mode 100644 index 000000000..77cc487f0 --- /dev/null +++ b/ReleaseNotes/2.1.2.md @@ -0,0 +1,9 @@ +# Workflow Core 2.1.2 + +* Adds a feature to purge old workflows from the persistence store. + +New `IWorkflowPurger` service that can be injected from the IoC container +```c# +Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan) +``` +Implementations are currently only for SQL Server, Postgres and MongoDB diff --git a/WorkflowCore.sln b/WorkflowCore.sln index b6f034222..612f99bcd 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -100,6 +100,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\1.9.3.md = ReleaseNotes\1.9.3.md ReleaseNotes\2.0.0.md = ReleaseNotes\2.0.0.md ReleaseNotes\2.1.0.md = ReleaseNotes\2.1.0.md + ReleaseNotes\2.1.2.md = ReleaseNotes\2.1.2.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" diff --git a/src/WorkflowCore/Interface/IWorkflowPurger.cs b/src/WorkflowCore/Interface/IWorkflowPurger.cs new file mode 100644 index 000000000..a85d9a0bf --- /dev/null +++ b/src/WorkflowCore/Interface/IWorkflowPurger.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface IWorkflowPurger + { + Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan); + } +} \ No newline at end of file diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 003a24f03..58ed8edc2 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 2.1.1 - 2.1.1.0 - 2.1.1.0 + 2.1.2 + 2.1.2.0 + 2.1.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs new file mode 100644 index 000000000..e75caaed0 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Persistence.EntityFramework.Interfaces; +using WorkflowCore.Persistence.EntityFramework.Models; + +namespace WorkflowCore.Persistence.EntityFramework.Services +{ + public class WorkflowPurger : IWorkflowPurger + { + private readonly IWorkflowDbContextFactory _contextFactory; + + public WorkflowPurger(IWorkflowDbContextFactory contextFactory) + { + _contextFactory = contextFactory; + } + + public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan) + { + var olderThanUtc = olderThan.ToUniversalTime(); + using (var db = ConstructDbContext()) + { + var workflows = await db.Set().Where(x => x.Status == status && x.CompleteTime < olderThanUtc).ToListAsync(); + foreach (var wf in workflows) + { + foreach (var pointer in wf.ExecutionPointers) + { + foreach (var extAttr in pointer.ExtensionAttributes) + { + db.Remove(extAttr); + } + + db.Remove(pointer); + } + db.Remove(wf); + } + + await db.SaveChangesAsync(); + } + } + + + private WorkflowDbContext ConstructDbContext() + { + return _contextFactory.Build(); + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 1f1c8340d..472a8035f 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -14,10 +14,10 @@ false false false - 2.0.1 + 2.1.1 Base package for Workflow-core peristence providers using entity framework - 2.0.1.0 - 2.0.1.0 + 2.1.1.0 + 2.1.1.0 diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs index c5a1f67da..a5d3ebb5c 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Persistence.MongoDB.Services; @@ -18,6 +19,12 @@ public static WorkflowOptions UseMongoDB(this WorkflowOptions options, string mo var db = client.GetDatabase(databaseName); return new MongoPersistenceProvider(db); }); + options.Services.AddTransient(sp => + { + var client = new MongoClient(mongoUrl); + var db = client.GetDatabase(databaseName); + return new WorkflowPurger(db); + }); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 7235adf58..940b99194 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -13,6 +13,7 @@ namespace WorkflowCore.Persistence.MongoDB.Services { public class MongoPersistenceProvider : IPersistenceProvider { + internal const string WorkflowCollectionName = "wfc.workflows"; private readonly IMongoDatabase _database; public MongoPersistenceProvider(IMongoDatabase database) @@ -77,7 +78,7 @@ static void CreateIndexes(MongoPersistenceProvider instance) } } - private IMongoCollection WorkflowInstances => _database.GetCollection("wfc.workflows"); + private IMongoCollection WorkflowInstances => _database.GetCollection(WorkflowCollectionName); private IMongoCollection EventSubscriptions => _database.GetCollection("wfc.subscriptions"); diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/WorkflowPurger.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/WorkflowPurger.cs new file mode 100644 index 000000000..85b1be31b --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/WorkflowPurger.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; +using MongoDB.Driver; +using WorkflowCore.Models; +using WorkflowCore.Interface; + +namespace WorkflowCore.Persistence.MongoDB.Services +{ + public class WorkflowPurger : IWorkflowPurger + { + private readonly IMongoDatabase _database; + private IMongoCollection WorkflowInstances => _database.GetCollection(MongoPersistenceProvider.WorkflowCollectionName); + + + public WorkflowPurger(IMongoDatabase database) + { + _database = database; + } + + public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan) + { + var olderThanUtc = olderThan.ToUniversalTime(); + await WorkflowInstances.DeleteManyAsync(x => x.Status == status && x.CompleteTime < olderThanUtc); + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index f4a94d337..143c3f601 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -14,10 +14,10 @@ false false false - 2.0.0 + 2.1.0 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 2.0.0.0 - 2.0.0.0 + 2.1.0.0 + 2.1.0.0 diff --git a/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs index 1db7980a5..b00fcc2c0 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/ServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using System; using Microsoft.EntityFrameworkCore.Infrastructure; +using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.MySQL; @@ -11,6 +12,7 @@ public static class ServiceCollectionExtensions public static WorkflowOptions UseMySQL(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB, Action mysqlOptionsAction = null) { options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new MysqlContextFactory(connectionString, mysqlOptionsAction), canCreateDB, canMigrateDB)); + options.Services.AddTransient(sp => new WorkflowPurger(new MysqlContextFactory(connectionString, mysqlOptionsAction))); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index a7e705846..9d5404999 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a MySQL database. - 1.2.0 - 1.2.0.0 - 1.2.0.0 + 1.2.1 + 1.2.1.0 + 1.2.1.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs index bf1bac81f..8df41c092 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.PostgreSQL; @@ -14,6 +15,7 @@ public static WorkflowOptions UsePostgreSQL(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB, string schemaName="wfc") { options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new PostgresContextFactory(connectionString, schemaName), canCreateDB, canMigrateDB)); + options.Services.AddTransient(sp => new WorkflowPurger(new PostgresContextFactory(connectionString, schemaName))); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index a7dcd8f60..3f2a86fc2 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,9 +15,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 2.0.2 - 2.0.2.0 - 2.0.2.0 + 2.1.1 + 2.1.1.0 + 2.1.1.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs index 7b999547a..46910a63d 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using System; +using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Persistence.EntityFramework.Interfaces; using WorkflowCore.Persistence.EntityFramework.Services; @@ -11,6 +12,7 @@ public static class ServiceCollectionExtensions public static WorkflowOptions UseSqlServer(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB) { options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new SqlContextFactory(connectionString), canCreateDB, canMigrateDB)); + options.Services.AddTransient(sp => new WorkflowPurger(new SqlContextFactory(connectionString))); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index d7076d514..a7be9e4ce 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,10 +15,10 @@ false false false - 2.0.1 + 2.1.1 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 2.0.1.0 - 2.0.1.0 + 2.1.1.0 + 2.1.1.0 diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs index 66edf2a88..f137c33b5 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.Sqlite; @@ -13,6 +14,7 @@ public static class ServiceCollectionExtensions public static WorkflowOptions UseSqlite(this WorkflowOptions options, string connectionString, bool canCreateDB) { options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new SqliteContextFactory(connectionString), canCreateDB, false)); + options.Services.AddTransient(sp => new WorkflowPurger(new SqliteContextFactory(connectionString))); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index fa97a4a7d..fcb84edd8 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 1.9.2 - 1.9.2.0 - 1.9.2.0 + 1.9.3 + 1.9.3.0 + 1.9.3.0 From dbd444fc0d1f45c344551b5e8a125008e36f80d2 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 20 Oct 2019 07:34:14 -0700 Subject: [PATCH 160/462] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64ab6068d..8a23da56a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build status](https://ci.appveyor.com/api/projects/status/xnby6p5v4ur04u76?svg=true)](https://ci.appveyor.com/project/danielgerlag/workflow-core) -Workflow Core is a light weight workflow engine targeting .NET Standard. Think: long running processes with multiple tasks that need to track state. It supports pluggable persistence and concurrency providers to allow for multi-node clusters. +Workflow Core is a light weight embeddable workflow engine targeting .NET Standard. Think: long running processes with multiple tasks that need to track state. It supports pluggable persistence and concurrency providers to allow for multi-node clusters. ### Announcements From 0ddc8b37da984a3e22052316b5f90a1b0b823f51 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 20 Oct 2019 07:35:19 -0700 Subject: [PATCH 161/462] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a23da56a..e54f50589 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Workflow Core is a light weight embeddable workflow engine targeting .NET Standa ### Announcements #### New related project: Conductor -Conductor is a stand-alone workflow server as opposed to a library that uses Workflow Core internally. It exposes an API that allows you to store workflow definitions, track running workflows, manage events and define lambdas and scripts for usage in your workflows. +Conductor is a stand-alone workflow server as opposed to a library that uses Workflow Core internally. It exposes an API that allows you to store workflow definitions, track running workflows, manage events and define custom steps and scripts for usage in your workflows. https://github.com/danielgerlag/conductor From 8dc4559c5283bc1a5fcd17bb791e596295af1245 Mon Sep 17 00:00:00 2001 From: OAlbrecht2407 <41269487+OAlbrecht2407@users.noreply.github.com> Date: Mon, 21 Oct 2019 12:38:17 +0200 Subject: [PATCH 162/462] Documentation fix Issue #425 --- docs/json-yaml.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/json-yaml.md b/docs/json-yaml.md index 431384240..305741ec6 100644 --- a/docs/json-yaml.md +++ b/docs/json-yaml.md @@ -3,7 +3,7 @@ Simply grab the `DefinitionLoader` from the IoC container and call the `.LoadDefinition` method ```c# -using using WorkflowCore.Services.DefinitionStorage; +using WorkflowCore.Interface; ... var loader = serviceProvider.GetService(); loader.LoadDefinition("<>", Deserializers.Json); From 780ffdfcd5b41a7ee0ecc5277c4599dc224ce615 Mon Sep 17 00:00:00 2001 From: Kirsanov Nikita Date: Fri, 4 Oct 2019 19:15:55 +0700 Subject: [PATCH 163/462] remove redundant dependency from PostgreSQL provider removed dependency: Npgsql.EntityFrameworkCore.PostgreSQL.Design --- .../WorkflowCore.Persistence.PostgreSQL.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 3f2a86fc2..55545f2b9 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -32,7 +32,6 @@ All - From 48e117fc0008cc9958ef48f00f4751555bed90e8 Mon Sep 17 00:00:00 2001 From: Kirsanov Nikita Date: Wed, 30 Oct 2019 15:14:34 +0700 Subject: [PATCH 164/462] Increment the version --- .../WorkflowCore.Persistence.PostgreSQL.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 55545f2b9..065475532 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,9 +15,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 2.1.1 - 2.1.1.0 - 2.1.1.0 + 2.1.2 + 2.1.2.0 + 2.1.2.0 From e1caacc8e00d1c3ac8a85aec08c5cbb3b32557e1 Mon Sep 17 00:00:00 2001 From: MatrixRonny <35771215+MatrixRonny@users.noreply.github.com> Date: Thu, 31 Oct 2019 14:31:36 +0200 Subject: [PATCH 165/462] Swapped examples. Swapped first example from "Saga transaction with compensation" with that from "Retry policy for failed saga transaction". --- docs/sagas.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sagas.md b/docs/sagas.md index 11d354657..083a6597a 100644 --- a/docs/sagas.md +++ b/docs/sagas.md @@ -15,7 +15,7 @@ builder .Then() .CompensateWith() ) - .OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5)) + .CompensateWith() .Then(context => Console.WriteLine("End")); ``` @@ -34,7 +34,7 @@ builder .Then() .CompensateWith() ) - .CompensateWith() + .OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5)) .Then(context => Console.WriteLine("End")); ``` @@ -158,4 +158,4 @@ Steps: - Id: Bye StepType: MyApp.GoodbyeWorld, MyApp -``` \ No newline at end of file +``` From 158f3baaf5e68181efdd74812a8e2099c0120d5a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 8 Nov 2019 21:07:49 -0800 Subject: [PATCH 166/462] upper version constraints against ef core 3 --- .../WorkflowCore.Persistence.EntityFramework.csproj | 10 +++++----- .../WorkflowCore.Persistence.PostgreSQL.csproj | 8 ++++---- .../WorkflowCore.Persistence.SqlServer.csproj | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 472a8035f..58ab3dccd 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -14,10 +14,10 @@ false false false - 2.1.1 + 2.1.2 Base package for Workflow-core peristence providers using entity framework - 2.1.1.0 - 2.1.1.0 + 2.1.2.0 + 2.1.2.0 @@ -25,8 +25,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 065475532..5b63773c3 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,9 +15,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 2.1.2 - 2.1.2.0 - 2.1.2.0 + 2.1.3 + 2.1.3.0 + 2.1.3.0 @@ -27,7 +27,7 @@ - + All diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index a7be9e4ce..ccbc21e4c 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,10 +15,10 @@ false false false - 2.1.1 + 2.1.2 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 2.1.1.0 - 2.1.1.0 + 2.1.2.0 + 2.1.2.0 @@ -27,7 +27,7 @@ - + All From 40c864336059700fccd244f202b2b11f1989496e Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 26 Nov 2019 14:12:45 +0100 Subject: [PATCH 167/462] Naming --- src/providers/WorkflowCore.Providers.Azure/README.md | 8 ++++---- .../ServiceCollectionExtensions.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.Azure/README.md b/src/providers/WorkflowCore.Providers.Azure/README.md index 858bab77a..a6641c564 100644 --- a/src/providers/WorkflowCore.Providers.Azure/README.md +++ b/src/providers/WorkflowCore.Providers.Azure/README.md @@ -22,13 +22,13 @@ dotnet add package WorkflowCore.Providers.Azure ## Usage Use the `IServiceCollection` extension methods when building your service provider -* .UseAzureSyncronization -* .UseAzureServiceBus +* .UseAzureSynchronization +* .UseAzureServiceBusEventHub ```C# services.AddWorkflow(options => { - options.UseAzureSyncronization("azure storage connection string"); - options.UseAzureServiceBus("service bus connection string", "topic name", "subscription name"); + options.UseAzureSynchronization("azure storage connection string"); + options.UseAzureServiceBusEventHub("service bus connection string", "topic name", "subscription name"); }); ``` \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs index a9152b7b4..5abb111fc 100644 --- a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs @@ -6,14 +6,14 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { - public static WorkflowOptions UseAzureSyncronization(this WorkflowOptions options, string connectionString) + public static WorkflowOptions UseAzureSynchronization(this WorkflowOptions options, string connectionString) { options.UseQueueProvider(sp => new AzureStorageQueueProvider(connectionString, sp.GetService())); options.UseDistributedLockManager(sp => new AzureLockManager(connectionString, sp.GetService())); return options; } - public static WorkflowOptions UseAzureServiceBus( + public static WorkflowOptions UseAzureServiceBusEventHub( this WorkflowOptions options, string connectionString, string topicName, From a00f3c63c3ee8ab3686a221c4560df1436262094 Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 26 Nov 2019 14:13:25 +0100 Subject: [PATCH 168/462] Naming --- src/samples/WorkflowCore.Sample04/Program.cs | 2 +- src/samples/WorkflowCore.Sample13/Program.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/samples/WorkflowCore.Sample04/Program.cs b/src/samples/WorkflowCore.Sample04/Program.cs index f977929cf..17f51fb9a 100644 --- a/src/samples/WorkflowCore.Sample04/Program.cs +++ b/src/samples/WorkflowCore.Sample04/Program.cs @@ -51,7 +51,7 @@ private static IServiceProvider ConfigureServices() //services.AddWorkflow(x => //{ - // x.UseAzureSyncronization(@"UseDevelopmentStorage=true"); + // x.UseAzureSynchronization(@"UseDevelopmentStorage=true"); // x.UseMongoDB(@"mongodb://localhost:27017", "workflow9999"); //}); diff --git a/src/samples/WorkflowCore.Sample13/Program.cs b/src/samples/WorkflowCore.Sample13/Program.cs index 8240a09cb..365e8ccdb 100644 --- a/src/samples/WorkflowCore.Sample13/Program.cs +++ b/src/samples/WorkflowCore.Sample13/Program.cs @@ -41,7 +41,7 @@ private static IServiceProvider ConfigureServices() //services.AddWorkflow(x => //{ - // x.UseAzureSyncronization(@"UseDevelopmentStorage=true"); + // x.UseAzureSynchronization(@"UseDevelopmentStorage=true"); // x.UseMongoDB(@"mongodb://localhost:27017", "workflow-test002"); //}); From 18e9522f78e16192f35aee5ec16830c74ea3b14c Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 3 Dec 2019 14:27:23 +0100 Subject: [PATCH 169/462] Replace obsolate index creation method --- .../Services/MongoPersistenceProvider.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 940b99194..0c00cea13 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -72,8 +72,14 @@ static void CreateIndexes(MongoPersistenceProvider instance) { if (!indexesCreated) { - instance.WorkflowInstances.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.NextExecution), new CreateIndexOptions() { Background = true, Name = "idx_nextExec" }); - instance.Events.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.IsProcessed), new CreateIndexOptions() { Background = true, Name = "idx_processed" }); + instance.WorkflowInstances.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys.Ascending(x => x.NextExecution), + new CreateIndexOptions {Background = true, Name = "idx_nextExec"})); + + instance.Events.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys.Ascending(x => x.IsProcessed), + new CreateIndexOptions {Background = true, Name = "idx_processed"})); + indexesCreated = true; } } From 1ef942a2a508bcea676042288cded3421a4d897a Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 3 Dec 2019 14:34:39 +0100 Subject: [PATCH 170/462] Add enum to string convention --- .../Services/MongoPersistenceProvider.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 0c00cea13..91e796314 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Conventions; using MongoDB.Driver.Linq; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -24,6 +26,13 @@ public MongoPersistenceProvider(IMongoDatabase database) static MongoPersistenceProvider() { + ConventionRegistry.Register( + "workflow.conventions", + new ConventionPack + { + new EnumRepresentationConvention(BsonType.String) + }, t => t.FullName?.StartsWith("WorkflowCore") ?? false); + BsonClassMap.RegisterClassMap(x => { x.MapIdProperty(y => y.Id) From 9c3b8f7d04cc14013aba5d351c3a2ebbbc6100dd Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 14 Dec 2019 17:01:46 -0800 Subject: [PATCH 171/462] update libraries --- src/WorkflowCore/Models/EventSubscription.cs | 2 + src/WorkflowCore/WorkflowCore.csproj | 16 +- .../WorkflowCore.Users.csproj | 2 +- .../WorkflowCore.WebAPI.csproj | 2 +- .../ExtensionMethods.cs | 2 + .../Models/PersistedSubscription.cs | 2 + ...lowCore.Persistence.EntityFramework.csproj | 10 +- .../Services/MongoPersistenceProvider.cs | 1 + .../WorkflowCore.Persistence.MongoDB.csproj | 6 +- ...0191214232718_SubscriptionData.Designer.cs | 245 +++++++++++++++++ .../20191214232718_SubscriptionData.cs | 24 ++ ...ostgresPersistenceProviderModelSnapshot.cs | 12 +- ...WorkflowCore.Persistence.PostgreSQL.csproj | 17 +- ...0191214232800_SubscriptionData.Designer.cs | 251 ++++++++++++++++++ .../20191214232800_SubscriptionData.cs | 24 ++ ...lServerPersistenceProviderModelSnapshot.cs | 12 +- .../WorkflowCore.Persistence.SqlServer.csproj | 13 +- .../WorkflowCore.Persistence.Sqlite.csproj | 8 +- .../ModelExtensions.cs | 4 +- .../WorkflowCore.Providers.AWS.csproj | 16 +- ...orkflowCore.Providers.Elasticsearch.csproj | 6 +- .../WorkflowCore.Providers.Redis.csproj | 6 +- ...rkflowCore.QueueProviders.SqlServer.csproj | 2 +- src/samples/WorkflowCore.Sample01/Program.cs | 3 - .../WorkflowCore.Sample01.csproj | 14 +- src/samples/WorkflowCore.Sample02/Program.cs | 3 - .../WorkflowCore.Sample02.csproj | 12 +- src/samples/WorkflowCore.Sample03/Program.cs | 4 +- .../WorkflowCore.Sample03.csproj | 9 +- src/samples/WorkflowCore.Sample04/Program.cs | 7 +- .../WorkflowCore.Sample04.csproj | 13 +- src/samples/WorkflowCore.Sample05/Program.cs | 3 - .../WorkflowCore.Sample05.csproj | 7 +- src/samples/WorkflowCore.Sample06/Program.cs | 3 - .../WorkflowCore.Sample06.csproj | 7 +- src/samples/WorkflowCore.Sample07/Startup.cs | 2 +- .../WorkflowCore.Sample07.csproj | 11 +- src/samples/WorkflowCore.Sample08/Program.cs | 3 - .../WorkflowCore.Sample08.csproj | 9 +- src/samples/WorkflowCore.Sample09/Program.cs | 3 - .../WorkflowCore.Sample09.csproj | 4 +- src/samples/WorkflowCore.Sample10/Program.cs | 3 - .../WorkflowCore.Sample10.csproj | 6 +- src/samples/WorkflowCore.Sample11/Program.cs | 3 - .../WorkflowCore.Sample11.csproj | 6 +- src/samples/WorkflowCore.Sample12/Program.cs | 3 - .../WorkflowCore.Sample12.csproj | 4 +- src/samples/WorkflowCore.Sample13/Program.cs | 3 - .../WorkflowCore.Sample13.csproj | 6 +- src/samples/WorkflowCore.Sample14/Program.cs | 3 - .../WorkflowCore.Sample14.csproj | 10 +- .../WorkflowCore.Sample15.csproj | 6 +- src/samples/WorkflowCore.Sample16/Program.cs | 3 - .../WorkflowCore.Sample16.csproj | 6 +- .../WorkflowCore.Sample17.csproj | 4 +- .../WorkflowCore.TestSample01.csproj | 5 +- .../Scenarios/BaseScenario.cs | 6 +- .../WorkflowCore.IntegrationTests.csproj | 8 +- .../WorkflowCore.Testing.csproj | 5 +- .../WorkflowCore.Tests.DynamoDB.csproj | 4 +- .../WorkflowCore.Tests.Elasticsearch.csproj | 4 +- .../WorkflowCore.Tests.MongoDB.csproj | 10 +- .../WorkflowCore.Tests.MySQL.csproj | 2 +- .../WorkflowCore.Tests.PostgreSQL.csproj | 7 +- .../WorkflowCore.Tests.Redis.csproj | 4 +- .../WorkflowCore.Tests.SqlServer.csproj | 2 +- .../WorkflowCore.Tests.Sqlite.csproj | 2 +- .../ExecutionResultProcessorFixture.cs | 2 +- .../Services/WorkflowExecutorFixture.cs | 2 +- .../WorkflowCore.UnitTests.csproj | 8 +- 70 files changed, 704 insertions(+), 233 deletions(-) create mode 100644 src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191214232718_SubscriptionData.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191214232718_SubscriptionData.cs create mode 100644 src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191214232800_SubscriptionData.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191214232800_SubscriptionData.cs diff --git a/src/WorkflowCore/Models/EventSubscription.cs b/src/WorkflowCore/Models/EventSubscription.cs index 593f1e3a5..3f7c3c737 100644 --- a/src/WorkflowCore/Models/EventSubscription.cs +++ b/src/WorkflowCore/Models/EventSubscription.cs @@ -15,5 +15,7 @@ public class EventSubscription public string EventKey { get; set; } public DateTime SubscribeAsOf { get; set; } + + public object SubscriptionData { get; set; } } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 58ed8edc2..c191d7bfe 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,25 +15,25 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 2.1.2 - 2.1.2.0 - 2.1.2.0 + 2.2.0 + 2.2.0.0 + 2.2.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - - - - + + + + - + diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 2725b4c61..4a6ea76fc 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj index f12a9630f..a3fb9bbb7 100644 --- a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj +++ b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs index c1f1ad875..f3c3d996e 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs @@ -102,6 +102,7 @@ internal static PersistedSubscription ToPersistable(this EventSubscription insta result.StepId = instance.StepId; result.WorkflowId = instance.WorkflowId; result.SubscribeAsOf = DateTime.SpecifyKind(instance.SubscribeAsOf, DateTimeKind.Utc); + result.SubscriptionData = JsonConvert.SerializeObject(instance.SubscriptionData, SerializerSettings); return result; } @@ -194,6 +195,7 @@ internal static EventSubscription ToEventSubscription(this PersistedSubscription result.StepId = instance.StepId; result.WorkflowId = instance.WorkflowId; result.SubscribeAsOf = DateTime.SpecifyKind(instance.SubscribeAsOf, DateTimeKind.Utc); + result.SubscriptionData = JsonConvert.DeserializeObject(instance.SubscriptionData, SerializerSettings); return result; } diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs index 0eb32e42d..c3be55164 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs @@ -27,5 +27,7 @@ public class PersistedSubscription public string EventKey { get; set; } public DateTime SubscribeAsOf { get; set; } + + public string SubscriptionData { get; set; } } } diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 58ab3dccd..129b4f335 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -14,10 +14,10 @@ false false false - 2.1.2 + 2.2.0 Base package for Workflow-core peristence providers using entity framework - 2.1.2.0 - 2.1.2.0 + 2.2.0.0 + 2.2.0.0 @@ -25,8 +25,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 940b99194..b845142cf 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -50,6 +50,7 @@ static MongoPersistenceProvider() x.MapProperty(y => y.StepId); x.MapProperty(y => y.WorkflowId); x.MapProperty(y => y.SubscribeAsOf); + x.MapProperty(y => y.SubscriptionData); }); BsonClassMap.RegisterClassMap(x => diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 143c3f601..19fdbad79 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -14,10 +14,10 @@ false false false - 2.1.0 + 2.2.0 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 2.1.0.0 - 2.1.0.0 + 2.2.0.0 + 2.2.0.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191214232718_SubscriptionData.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191214232718_SubscriptionData.Designer.cs new file mode 100644 index 000000000..0ba8dbd0a --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191214232718_SubscriptionData.Designer.cs @@ -0,0 +1,245 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using WorkflowCore.Persistence.PostgreSQL; + +namespace WorkflowCore.Persistence.PostgreSQL.Migrations +{ + [DbContext(typeof(PostgresContext))] + [Migration("20191214232718_SubscriptionData")] + partial class SubscriptionData + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("EventData"); + + b.Property("EventId"); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("EventTime"); + + b.Property("IsProcessed"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("ErrorTime"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100); + + b.Property("Message"); + + b.Property("WorkflowId") + .HasMaxLength(100); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("Children"); + + b.Property("ContextItem"); + + b.Property("EndTime"); + + b.Property("EventData"); + + b.Property("EventKey") + .HasMaxLength(100); + + b.Property("EventName") + .HasMaxLength(100); + + b.Property("EventPublished"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("Outcome"); + + b.Property("PersistenceData"); + + b.Property("PredecessorId") + .HasMaxLength(100); + + b.Property("RetryCount"); + + b.Property("Scope"); + + b.Property("SleepUntil"); + + b.Property("StartTime"); + + b.Property("Status"); + + b.Property("StepId"); + + b.Property("StepName") + .HasMaxLength(100); + + b.Property("WorkflowId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("AttributeKey") + .HasMaxLength(100); + + b.Property("AttributeValue"); + + b.Property("ExecutionPointerId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("StepId"); + + b.Property("SubscribeAsOf"); + + b.Property("SubscriptionData"); + + b.Property("SubscriptionId") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd(); + + b.Property("CompleteTime"); + + b.Property("CreateTime"); + + b.Property("Data"); + + b.Property("Description") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasMaxLength(200); + + b.Property("NextExecution"); + + b.Property("Reference") + .HasMaxLength(200); + + b.Property("Status"); + + b.Property("Version"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191214232718_SubscriptionData.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191214232718_SubscriptionData.cs new file mode 100644 index 000000000..e95fcf6f7 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191214232718_SubscriptionData.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.PostgreSQL.Migrations +{ + public partial class SubscriptionData : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SubscriptionData", + schema: "wfc", + table: "Subscription", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SubscriptionData", + schema: "wfc", + table: "Subscription"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs index 5d03522a1..d37d4eb48 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs @@ -1,12 +1,9 @@ // +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using System; -using WorkflowCore.Models; using WorkflowCore.Persistence.PostgreSQL; namespace WorkflowCore.Persistence.PostgreSQL.Migrations @@ -19,7 +16,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) - .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 63); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => { @@ -165,6 +163,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("SubscribeAsOf"); + b.Property("SubscriptionData"); + b.Property("SubscriptionId") .HasMaxLength(200); diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 5b63773c3..f3436fdf7 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,9 +15,9 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 2.1.3 - 2.1.3.0 - 2.1.3.0 + 2.2.0 + 2.2.0.0 + 2.2.0.0 @@ -26,12 +26,15 @@ - - - + + + All - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191214232800_SubscriptionData.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191214232800_SubscriptionData.Designer.cs new file mode 100644 index 000000000..2ba59aee2 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191214232800_SubscriptionData.Designer.cs @@ -0,0 +1,251 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WorkflowCore.Persistence.SqlServer; + +namespace WorkflowCore.Persistence.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerContext))] + [Migration("20191214232800_SubscriptionData")] + partial class SubscriptionData + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("EventData"); + + b.Property("EventId"); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("EventTime"); + + b.Property("IsProcessed"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ErrorTime"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100); + + b.Property("Message"); + + b.Property("WorkflowId") + .HasMaxLength(100); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Active"); + + b.Property("Children"); + + b.Property("ContextItem"); + + b.Property("EndTime"); + + b.Property("EventData"); + + b.Property("EventKey") + .HasMaxLength(100); + + b.Property("EventName") + .HasMaxLength(100); + + b.Property("EventPublished"); + + b.Property("Id") + .HasMaxLength(50); + + b.Property("Outcome"); + + b.Property("PersistenceData"); + + b.Property("PredecessorId") + .HasMaxLength(100); + + b.Property("RetryCount"); + + b.Property("Scope"); + + b.Property("SleepUntil"); + + b.Property("StartTime"); + + b.Property("Status"); + + b.Property("StepId"); + + b.Property("StepName") + .HasMaxLength(100); + + b.Property("WorkflowId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AttributeKey") + .HasMaxLength(100); + + b.Property("AttributeValue"); + + b.Property("ExecutionPointerId"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("EventKey") + .HasMaxLength(200); + + b.Property("EventName") + .HasMaxLength(200); + + b.Property("StepId"); + + b.Property("SubscribeAsOf"); + + b.Property("SubscriptionData"); + + b.Property("SubscriptionId") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CompleteTime"); + + b.Property("CreateTime"); + + b.Property("Data"); + + b.Property("Description") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasMaxLength(200); + + b.Property("NextExecution"); + + b.Property("Reference") + .HasMaxLength(200); + + b.Property("Status"); + + b.Property("Version"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191214232800_SubscriptionData.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191214232800_SubscriptionData.cs new file mode 100644 index 000000000..cc0f797e0 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191214232800_SubscriptionData.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.SqlServer.Migrations +{ + public partial class SubscriptionData : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SubscriptionData", + schema: "wfc", + table: "Subscription", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SubscriptionData", + schema: "wfc", + table: "Subscription"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs index 313b4674b..12b349dcc 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs @@ -1,12 +1,9 @@ // +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using System; -using WorkflowCore.Models; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using WorkflowCore.Persistence.SqlServer; namespace WorkflowCore.Persistence.SqlServer.Migrations @@ -18,7 +15,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.0.1-rtm-125") + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => @@ -170,6 +168,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("SubscribeAsOf"); + b.Property("SubscriptionData"); + b.Property("SubscriptionId") .HasMaxLength(200); diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index ccbc21e4c..198b6b9a9 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,10 +15,10 @@ false false false - 2.1.2 + 2.2.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 2.1.2.0 - 2.1.2.0 + 2.2.0.0 + 2.2.0.0 @@ -27,11 +27,14 @@ - + All - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index fcb84edd8..35022ed26 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 1.9.3 - 1.9.3.0 - 1.9.3.0 + 2.2.0 + 2.2.0.0 + 2.2.0.0 @@ -27,7 +27,7 @@ - + diff --git a/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs index 5d38f094f..49183847f 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs @@ -90,6 +90,7 @@ public static Dictionary ToDynamoMap(this EventSubscript ["workflow_id"] = new AttributeValue(source.WorkflowId), ["step_id"] = new AttributeValue(source.StepId.ToString()), ["subscribe_as_of"] = new AttributeValue() { N = source.SubscribeAsOf.Ticks.ToString() }, + ["subscription_data"] = new AttributeValue(JsonConvert.SerializeObject(source.SubscriptionData, SerializerSettings)), ["event_slug"] = new AttributeValue($"{source.EventName}:{source.EventKey}") }; } @@ -103,7 +104,8 @@ public static EventSubscription ToEventSubscription(this Dictionary + netstandard2.0 @@ -7,19 +7,19 @@ - Provides Queueing support on Workflow Core - Provides distributed locking support on Workflow Core - https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md + https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 2.0.0 - 2.0.0.0 + 2.2.0 + 2.2.0.0 - - - - + + + + diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index 2f66017f3..a0374a50c 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 2.0.0 + 2.2.0 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git @@ -13,8 +13,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index e4489cb54..bf01cee71 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 2.0.0 + 2.2.1 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git @@ -12,10 +12,10 @@ - + - + diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 205907c87..36d7cda77 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample01/Program.cs b/src/samples/WorkflowCore.Sample01/Program.cs index 4a4c82bc7..c25838dbe 100644 --- a/src/samples/WorkflowCore.Sample01/Program.cs +++ b/src/samples/WorkflowCore.Sample01/Program.cs @@ -39,9 +39,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(); return serviceProvider; } diff --git a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj index bd750c20f..0733913fd 100644 --- a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj +++ b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.2 WorkflowCore.Sample01 Exe WorkflowCore.Sample01 @@ -16,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample02/Program.cs b/src/samples/WorkflowCore.Sample02/Program.cs index 77b395a26..3c1a65bcf 100644 --- a/src/samples/WorkflowCore.Sample02/Program.cs +++ b/src/samples/WorkflowCore.Sample02/Program.cs @@ -35,9 +35,6 @@ private static IServiceProvider ConfigureServices() services.AddWorkflow(); var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(); return serviceProvider; } } diff --git a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj index 3e2a90b7d..4e965f8e5 100644 --- a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj +++ b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.2 WorkflowCore.Sample02 Exe WorkflowCore.Sample02 @@ -11,16 +11,12 @@ - + + - - - - - - + diff --git a/src/samples/WorkflowCore.Sample03/Program.cs b/src/samples/WorkflowCore.Sample03/Program.cs index 1f17ae507..96c06d45d 100644 --- a/src/samples/WorkflowCore.Sample03/Program.cs +++ b/src/samples/WorkflowCore.Sample03/Program.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Debug; using System; using System.Collections.Generic; using System.Linq; @@ -52,9 +53,6 @@ private static IServiceProvider ConfigureServices() //services.AddWorkflow(x => x.UseSqlServer(@"Server=.\SQLEXPRESS;Database=WorkflowCore;Trusted_Connection=True;", true, true)); var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(); return serviceProvider; } } diff --git a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj index d8f16ebe7..da418de31 100644 --- a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj +++ b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj @@ -16,12 +16,9 @@ - - - - - - + + + diff --git a/src/samples/WorkflowCore.Sample04/Program.cs b/src/samples/WorkflowCore.Sample04/Program.cs index 17f51fb9a..bd9b67b72 100644 --- a/src/samples/WorkflowCore.Sample04/Program.cs +++ b/src/samples/WorkflowCore.Sample04/Program.cs @@ -43,9 +43,9 @@ private static IServiceProvider ConfigureServices() //setup dependency injection IServiceCollection services = new ServiceCollection(); services.AddLogging(); - services.AddWorkflow(); + //services.AddWorkflow(); //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); - //services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); + services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); //services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;", true, true)); //services.AddWorkflow(x => x.UseSqlite(@"Data Source=database.db;", true)); @@ -88,9 +88,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(LogLevel.Debug); return serviceProvider; } diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index 1b994fbd8..e0a892ecc 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp2.2 WorkflowCore.Sample04 Exe WorkflowCore.Sample04 @@ -23,13 +23,10 @@ - - - - - - - + + + + diff --git a/src/samples/WorkflowCore.Sample05/Program.cs b/src/samples/WorkflowCore.Sample05/Program.cs index 4e9f77b07..274e10232 100644 --- a/src/samples/WorkflowCore.Sample05/Program.cs +++ b/src/samples/WorkflowCore.Sample05/Program.cs @@ -38,9 +38,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(); return serviceProvider; } } diff --git a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj index 31cff586e..592fa228b 100644 --- a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj +++ b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj @@ -16,12 +16,7 @@ - - - - - - + diff --git a/src/samples/WorkflowCore.Sample06/Program.cs b/src/samples/WorkflowCore.Sample06/Program.cs index b0bb656a4..ba08aa071 100644 --- a/src/samples/WorkflowCore.Sample06/Program.cs +++ b/src/samples/WorkflowCore.Sample06/Program.cs @@ -38,9 +38,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(); return serviceProvider; } } diff --git a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj index da671ebf3..b61478b9e 100644 --- a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj +++ b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj @@ -16,12 +16,7 @@ - - - - - - + diff --git a/src/samples/WorkflowCore.Sample07/Startup.cs b/src/samples/WorkflowCore.Sample07/Startup.cs index 6c8f619b8..42abb05cb 100644 --- a/src/samples/WorkflowCore.Sample07/Startup.cs +++ b/src/samples/WorkflowCore.Sample07/Startup.cs @@ -24,7 +24,7 @@ public void ConfigureServices(IServiceCollection services) public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { - loggerFactory.AddConsole(); + //loggerFactory.AddConsole(); //start the workflow host var host = app.ApplicationServices.GetService(); diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index fef512688..a6929f4e8 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp2.2 true WorkflowCore.Sample07 Exe @@ -23,10 +23,11 @@ - - - - + + + + + diff --git a/src/samples/WorkflowCore.Sample08/Program.cs b/src/samples/WorkflowCore.Sample08/Program.cs index 1c4ba2036..6a5d1d9ad 100644 --- a/src/samples/WorkflowCore.Sample08/Program.cs +++ b/src/samples/WorkflowCore.Sample08/Program.cs @@ -79,9 +79,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(LogLevel.Debug); return serviceProvider; } diff --git a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj index 9b3e8ae83..40c7596e4 100644 --- a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj +++ b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.2 WorkflowCore.Sample08 Exe WorkflowCore.Sample08 @@ -21,12 +21,7 @@ - - - - - - + diff --git a/src/samples/WorkflowCore.Sample09/Program.cs b/src/samples/WorkflowCore.Sample09/Program.cs index 4455f86aa..52516fca8 100644 --- a/src/samples/WorkflowCore.Sample09/Program.cs +++ b/src/samples/WorkflowCore.Sample09/Program.cs @@ -38,9 +38,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(LogLevel.Debug); return serviceProvider; } diff --git a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj index 8590c443e..5db7f6ca0 100644 --- a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj +++ b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj @@ -6,9 +6,7 @@ - - - + diff --git a/src/samples/WorkflowCore.Sample10/Program.cs b/src/samples/WorkflowCore.Sample10/Program.cs index c57970e22..75173f70c 100644 --- a/src/samples/WorkflowCore.Sample10/Program.cs +++ b/src/samples/WorkflowCore.Sample10/Program.cs @@ -37,9 +37,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - //loggerFactory.AddDebug(LogLevel.Debug); return serviceProvider; } } diff --git a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj index 8590c443e..07135f7b7 100644 --- a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj +++ b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj @@ -2,13 +2,11 @@ Exe - netcoreapp2.0 + netcoreapp2.2 - - - + diff --git a/src/samples/WorkflowCore.Sample11/Program.cs b/src/samples/WorkflowCore.Sample11/Program.cs index a3097ab5f..f16853aa4 100644 --- a/src/samples/WorkflowCore.Sample11/Program.cs +++ b/src/samples/WorkflowCore.Sample11/Program.cs @@ -36,9 +36,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - //loggerFactory.AddDebug(LogLevel.Debug); return serviceProvider; } } diff --git a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj index 525d8aad4..b6454f6a2 100644 --- a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj +++ b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp2.0 + netcoreapp2.2 - - + + diff --git a/src/samples/WorkflowCore.Sample12/Program.cs b/src/samples/WorkflowCore.Sample12/Program.cs index be1144a6a..6a9ebe317 100644 --- a/src/samples/WorkflowCore.Sample12/Program.cs +++ b/src/samples/WorkflowCore.Sample12/Program.cs @@ -37,9 +37,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(LogLevel.Debug); return serviceProvider; } } diff --git a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj index 1d4cf4a14..2d1fc738b 100644 --- a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj +++ b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj @@ -6,9 +6,7 @@ - - - + diff --git a/src/samples/WorkflowCore.Sample13/Program.cs b/src/samples/WorkflowCore.Sample13/Program.cs index 365e8ccdb..9f5a22e7a 100644 --- a/src/samples/WorkflowCore.Sample13/Program.cs +++ b/src/samples/WorkflowCore.Sample13/Program.cs @@ -53,9 +53,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - //loggerFactory.AddDebug(LogLevel.Debug); return serviceProvider; } } diff --git a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj index dcb58fd03..dbe49166e 100644 --- a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj +++ b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj @@ -2,13 +2,11 @@ Exe - netcoreapp2.0 + netcoreapp2.2 - - - + diff --git a/src/samples/WorkflowCore.Sample14/Program.cs b/src/samples/WorkflowCore.Sample14/Program.cs index 3e9f50db4..b2ecabb2d 100644 --- a/src/samples/WorkflowCore.Sample14/Program.cs +++ b/src/samples/WorkflowCore.Sample14/Program.cs @@ -33,9 +33,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - //loggerFactory.AddDebug(LogLevel.Debug); return serviceProvider; } } diff --git a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj index 69e6c65b6..fef8173cc 100644 --- a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj +++ b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj @@ -2,17 +2,9 @@ Exe - netcoreapp2.0 + netcoreapp2.2 - - - - - - - - diff --git a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj index 48f29e329..c63fbd792 100644 --- a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj +++ b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp2.0 + netcoreapp2.2 - - + + diff --git a/src/samples/WorkflowCore.Sample16/Program.cs b/src/samples/WorkflowCore.Sample16/Program.cs index 1f95fc65e..6ca03139d 100644 --- a/src/samples/WorkflowCore.Sample16/Program.cs +++ b/src/samples/WorkflowCore.Sample16/Program.cs @@ -32,9 +32,6 @@ private static IServiceProvider ConfigureServices() var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - //loggerFactory.AddDebug(LogLevel.Debug); return serviceProvider; } } diff --git a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj index 7746f7ecb..c63fbd792 100644 --- a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj +++ b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp2.0 + netcoreapp2.2 - - + + diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index ee8c79856..36d4950d0 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp2.0 + netcoreapp2.2 - + diff --git a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj index 3ce804b11..282fdbb15 100644 --- a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj +++ b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj @@ -1,13 +1,12 @@  - netcoreapp2.0 + netcoreapp2.2 - - + diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/BaseScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/BaseScenario.cs index b7bb6d78e..4e70b446c 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/BaseScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/BaseScenario.cs @@ -24,15 +24,11 @@ protected void Setup() { //setup dependency injection IServiceCollection services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(x => x.AddConsole()); Configure(services); var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddConsole(LogLevel.Debug); - PersistenceProvider = serviceProvider.GetService(); Host = serviceProvider.GetService(); Host.RegisterWorkflow(); diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index a2a88fb71..971f10c57 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.2 WorkflowCore.IntegrationTests WorkflowCore.IntegrationTests true @@ -21,10 +21,10 @@ - - + + - + diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index dd02cc70d..7d183ae08 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -9,8 +9,9 @@ - - + + + diff --git a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index 2296962c3..c2563b8b2 100644 --- a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -1,13 +1,13 @@ - netcoreapp2.1 + netcoreapp2.2 false - + diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 173c6ee1b..f38e2aae5 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -1,14 +1,14 @@ - netcoreapp2.1 + netcoreapp2.2 false - + diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index 884e2937e..bec4d8561 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.2 WorkflowCore.Tests.MongoDB WorkflowCore.Tests.MongoDB true @@ -22,12 +22,12 @@ - - - + + + - + diff --git a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj index 9a7efe2ff..a255e01d6 100644 --- a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj +++ b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj @@ -1,7 +1,7 @@ - netcoreapp2.0 + netcoreapp2.2 false diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index fe609fb15..42c419a1a 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.2 WorkflowCore.Tests.PostgreSQL WorkflowCore.Tests.PostgreSQL true @@ -21,11 +21,8 @@ + - - - - diff --git a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj index 6fa33c51a..c5ee9a6f8 100644 --- a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj +++ b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj @@ -1,14 +1,14 @@ - netcoreapp2.1 + netcoreapp2.2 false - + diff --git a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj index 6c642fa8c..011714983 100644 --- a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj +++ b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.2 diff --git a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj index a8cfbe250..9ce1b77cd 100644 --- a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj +++ b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.2 diff --git a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs index dbaa90e53..03eaa7cc7 100644 --- a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs @@ -37,7 +37,7 @@ public ExecutionResultProcessorFixture() //config logging var loggerFactory = new LoggerFactory(); - loggerFactory.AddConsole(LogLevel.Debug); + //loggerFactory.AddConsole(LogLevel.Debug); Subject = new ExecutionResultProcessor(PointerFactory, DateTimeProvider, EventHub, ErrorHandlers, Options, loggerFactory); } diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index fec58c547..781c04e8f 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -51,7 +51,7 @@ public WorkflowExecutorFixture() //config logging var loggerFactory = new LoggerFactory(); - loggerFactory.AddConsole(LogLevel.Debug); + //loggerFactory.AddConsole(LogLevel.Debug); Subject = new WorkflowExecutor(Registry, ServiceProvider, ScopeProvider, DateTimeProvider, ResultProcesser, EventHub, CancellationProcessor, Options, loggerFactory); } diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index a21bec422..95765e41a 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.2 WorkflowCore.UnitTests WorkflowCore.UnitTests true @@ -19,9 +19,9 @@ - - - + + + From d831c5cfbf9f8fa8464f97a9dd939bedc47588c7 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 14 Dec 2019 17:04:03 -0800 Subject: [PATCH 172/462] sample --- src/samples/WorkflowCore.Sample04/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/samples/WorkflowCore.Sample04/Program.cs b/src/samples/WorkflowCore.Sample04/Program.cs index bd9b67b72..09e80b737 100644 --- a/src/samples/WorkflowCore.Sample04/Program.cs +++ b/src/samples/WorkflowCore.Sample04/Program.cs @@ -43,9 +43,9 @@ private static IServiceProvider ConfigureServices() //setup dependency injection IServiceCollection services = new ServiceCollection(); services.AddLogging(); - //services.AddWorkflow(); + services.AddWorkflow(); //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); - services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); + //services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); //services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;", true, true)); //services.AddWorkflow(x => x.UseSqlite(@"Data Source=database.db;", true)); From b4dee83cc7e398210aaf696d05cf200e2ecb2d89 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 14 Dec 2019 18:05:05 -0800 Subject: [PATCH 173/462] Update WorkflowCore.Testing.csproj --- test/WorkflowCore.Testing/WorkflowCore.Testing.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 7d183ae08..968a41116 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 1.9.0 - 1.9.0.0 - 1.9.0.0 + 2.2.0 + 2.2.0.0 + 2.2.0.0 Facilitates testing of workflows built on Workflow-Core From e740447358a1e04890f737cee2d3c0e57b5e01f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Moe?= Date: Mon, 16 Dec 2019 10:47:03 +0100 Subject: [PATCH 174/462] Add UtcNow to IDateTimeProvider, and use that throughout. Fix some assignments to EventTimeUtc. Old version with DateTime.Now.ToUniversalTime was less clear and above an order of magnitude slower. --- .../Interface/IDateTimeProvider.cs | 1 + .../Services/BackgroundTasks/EventConsumer.cs | 2 +- .../BackgroundTasks/WorkflowConsumer.cs | 4 ++-- src/WorkflowCore/Services/DateTimeProvider.cs | 1 + .../ErrorHandlers/CompensateHandler.cs | 2 +- .../Services/ErrorHandlers/RetryHandler.cs | 2 +- .../Services/ErrorHandlers/SuspendHandler.cs | 2 +- .../ErrorHandlers/TerminateHandler.cs | 2 +- .../Services/ExecutionResultProcessor.cs | 8 +++---- src/WorkflowCore/Services/WorkflowExecutor.cs | 24 +++++++++---------- .../ExecutionResultProcessorFixture.cs | 1 + .../Services/WorkflowExecutorFixture.cs | 1 + 12 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/WorkflowCore/Interface/IDateTimeProvider.cs b/src/WorkflowCore/Interface/IDateTimeProvider.cs index eec4026f3..13c5b86f3 100644 --- a/src/WorkflowCore/Interface/IDateTimeProvider.cs +++ b/src/WorkflowCore/Interface/IDateTimeProvider.cs @@ -5,5 +5,6 @@ namespace WorkflowCore.Interface public interface IDateTimeProvider { DateTime Now { get; } + DateTime UtcNow { get; } } } \ No newline at end of file diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index 71f6a9c86..0fa13ef84 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -37,7 +37,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { cancellationToken.ThrowIfCancellationRequested(); var evt = await _persistenceStore.GetEvent(itemId); - if (evt.EventTime <= _datetimeProvider.Now.ToUniversalTime()) + if (evt.EventTime <= _datetimeProvider.UtcNow) { var subs = await _persistenceStore.GetSubcriptions(evt.EventName, evt.EventKey, evt.EventTime); var toQueue = new List(); diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 4cfa74716..1548ddd10 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -71,7 +71,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance await persistenceStore.PersistErrors(result.Errors); - var readAheadTicks = _datetimeProvider.Now.Add(Options.PollInterval).ToUniversalTime().Ticks; + var readAheadTicks = _datetimeProvider.UtcNow.Add(Options.PollInterval).Ticks; if ((workflow.Status == WorkflowStatus.Runnable) && workflow.NextExecution.HasValue && workflow.NextExecution.Value < readAheadTicks) { @@ -109,7 +109,7 @@ private async void FutureQueue(WorkflowInstance workflow, CancellationToken canc return; } - var target = (workflow.NextExecution.Value - _datetimeProvider.Now.ToUniversalTime().Ticks); + var target = (workflow.NextExecution.Value - _datetimeProvider.UtcNow.Ticks); if (target > 0) { await Task.Delay(TimeSpan.FromTicks(target), cancellationToken); diff --git a/src/WorkflowCore/Services/DateTimeProvider.cs b/src/WorkflowCore/Services/DateTimeProvider.cs index 467b08248..93defc48f 100644 --- a/src/WorkflowCore/Services/DateTimeProvider.cs +++ b/src/WorkflowCore/Services/DateTimeProvider.cs @@ -6,5 +6,6 @@ namespace WorkflowCore.Services public class DateTimeProvider : IDateTimeProvider { public DateTime Now => DateTime.Now; + public DateTime UtcNow => DateTime.UtcNow; } } diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index aca56d6b6..1f3142610 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -60,7 +60,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP } scopePointer.Active = false; - scopePointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); + scopePointer.EndTime = _datetimeProvider.UtcNow; scopePointer.Status = PointerStatus.Failed; if (scopeStep.CompensationStepId.HasValue) diff --git a/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs index f934922f6..0a4d54810 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs @@ -22,7 +22,7 @@ public RetryHandler(IDateTimeProvider datetimeProvider, WorkflowOptions options) public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue bubbleUpQueue) { pointer.RetryCount++; - pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(step.RetryInterval ?? def.DefaultErrorRetryInterval ?? _options.ErrorRetryInterval); + pointer.SleepUntil = _datetimeProvider.UtcNow.Add(step.RetryInterval ?? def.DefaultErrorRetryInterval ?? _options.ErrorRetryInterval); step.PrimeForRetry(pointer); } } diff --git a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs index b0aec84fd..1cf6f7941 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs @@ -24,7 +24,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP workflow.Status = WorkflowStatus.Suspended; _eventPublisher.PublishNotification(new WorkflowSuspended() { - EventTimeUtc = _datetimeProvider.Now, + EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, diff --git a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs index 3745fe483..d89f7f470 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs @@ -24,7 +24,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP workflow.Status = WorkflowStatus.Terminated; _eventPublisher.PublishNotification(new WorkflowTerminated() { - EventTimeUtc = _datetimeProvider.Now, + EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index 730dbc911..65ad3fda9 100755 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -33,7 +33,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition pointer.Outcome = result.OutcomeValue; if (result.SleepFor.HasValue) { - pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(result.SleepFor.Value); + pointer.SleepUntil = _datetimeProvider.UtcNow.Add(result.SleepFor.Value); pointer.Status = PointerStatus.Sleeping; } @@ -57,7 +57,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition if (result.Proceed) { pointer.Active = false; - pointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); + pointer.EndTime = _datetimeProvider.UtcNow; pointer.Status = PointerStatus.Complete; foreach (var outcomeTarget in step.Outcomes.Where(x => object.Equals(x.GetValue(workflow.Data), result.OutcomeValue) || x.GetValue(workflow.Data) == null)) @@ -67,7 +67,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition _eventPublisher.PublishNotification(new StepCompleted() { - EventTimeUtc = _datetimeProvider.Now, + EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, ExecutionPointerId = pointer.Id, StepId = step.Id, @@ -92,7 +92,7 @@ public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition de { _eventPublisher.PublishNotification(new WorkflowError() { - EventTimeUtc = _datetimeProvider.Now, + EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 9c28ce7f3..7597e7490 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -41,7 +41,7 @@ public async Task Execute(WorkflowInstance workflow) { var wfResult = new WorkflowExecutorResult(); - var exePointers = new List(workflow.ExecutionPointers.Where(x => x.Active && (!x.SleepUntil.HasValue || x.SleepUntil < _datetimeProvider.Now.ToUniversalTime()))); + var exePointers = new List(workflow.ExecutionPointers.Where(x => x.Active && (!x.SleepUntil.HasValue || x.SleepUntil < _datetimeProvider.UtcNow))); var def = _registry.GetDefinition(workflow.WorkflowDefinitionId, workflow.Version); if (def == null) { @@ -60,12 +60,12 @@ public async Task Execute(WorkflowInstance workflow) if (step == null) { _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); - pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); + pointer.SleepUntil = _datetimeProvider.UtcNow.Add(_options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, - ErrorTime = _datetimeProvider.Now.ToUniversalTime(), + ErrorTime = _datetimeProvider.UtcNow, Message = $"Unable to find step {pointer.StepId} in workflow definition" }); continue; @@ -85,7 +85,7 @@ public async Task Execute(WorkflowInstance workflow) { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, - ErrorTime = _datetimeProvider.Now.ToUniversalTime(), + ErrorTime = _datetimeProvider.UtcNow, Message = ex.Message }); @@ -108,7 +108,7 @@ private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, Workfl return false; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; - workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); + workflow.CompleteTime = _datetimeProvider.UtcNow; return false; } @@ -117,7 +117,7 @@ private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, Workfl pointer.Status = PointerStatus.Running; _publisher.PublishNotification(new StepStarted() { - EventTimeUtc = _datetimeProvider.Now, + EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, ExecutionPointerId = pointer.Id, StepId = step.Id, @@ -129,7 +129,7 @@ private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, Workfl if (!pointer.StartTime.HasValue) { - pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); + pointer.StartTime = _datetimeProvider.UtcNow; } return true; @@ -146,12 +146,12 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe if (body == null) { _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); - pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); + pointer.SleepUntil = _datetimeProvider.UtcNow.Add(_options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, - ErrorTime = _datetimeProvider.Now.ToUniversalTime(), + ErrorTime = _datetimeProvider.UtcNow, Message = $"Unable to construct step body {step.BodyType.ToString()}" }); return; @@ -175,7 +175,7 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe return; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; - workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); + workflow.CompleteTime = _datetimeProvider.UtcNow; return; } @@ -245,10 +245,10 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) return; workflow.Status = WorkflowStatus.Complete; - workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); + workflow.CompleteTime = _datetimeProvider.UtcNow; _publisher.PublishNotification(new WorkflowCompleted() { - EventTimeUtc = _datetimeProvider.Now, + EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, diff --git a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs index 03eaa7cc7..d1378e2b3 100644 --- a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs @@ -34,6 +34,7 @@ public ExecutionResultProcessorFixture() Options = new WorkflowOptions(A.Fake()); A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); + A.CallTo(() => DateTimeProvider.UtcNow).Returns(DateTime.UtcNow); //config logging var loggerFactory = new LoggerFactory(); diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 781c04e8f..6d3f04499 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -48,6 +48,7 @@ public WorkflowExecutorFixture() A.CallTo(() => scope.ServiceProvider).Returns(ServiceProvider); A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); + A.CallTo(() => DateTimeProvider.UtcNow).Returns(DateTime.UtcNow); //config logging var loggerFactory = new LoggerFactory(); From 7e07419a9672e534626b5196aabac2fec883104b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 21 Dec 2019 14:06:51 -0800 Subject: [PATCH 175/462] Split DSL into own package --- WorkflowCore.sln | 11 +++++++-- docs/json-yaml.md | 3 ++- .../Interface/IDefinitionLoader.cs | 0 .../Models}/DefinitionSource.cs | 0 .../Models}/Envelope.cs | 0 .../Models}/v1/DefinitionSourceV1.cs | 0 .../Models}/v1/MappingSourceV1.cs | 0 .../Models}/v1/StepSourceV1.cs | 0 .../ServiceCollectionExtensions.cs | 20 ++++++++++++++++ .../Services}/DefinitionLoader.cs | 0 .../Services}/Deserializers.cs | 0 src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 24 +++++++++++++++++++ .../ServiceCollectionExtensions.cs | 2 -- src/WorkflowCore/WorkflowCore.csproj | 4 ++++ test/WorkflowCore.Testing/JsonWorkflowTest.cs | 1 + .../WorkflowCore.Testing.csproj | 1 + test/WorkflowCore.Testing/YamlWorkflowTest.cs | 1 + .../WorkflowCore.UnitTests.csproj | 1 + 18 files changed, 63 insertions(+), 5 deletions(-) rename src/{WorkflowCore => WorkflowCore.DSL}/Interface/IDefinitionLoader.cs (100%) rename src/{WorkflowCore/Models/DefinitionStorage => WorkflowCore.DSL/Models}/DefinitionSource.cs (100%) rename src/{WorkflowCore/Models/DefinitionStorage => WorkflowCore.DSL/Models}/Envelope.cs (100%) rename src/{WorkflowCore/Models/DefinitionStorage => WorkflowCore.DSL/Models}/v1/DefinitionSourceV1.cs (100%) rename src/{WorkflowCore/Models/DefinitionStorage => WorkflowCore.DSL/Models}/v1/MappingSourceV1.cs (100%) rename src/{WorkflowCore/Models/DefinitionStorage => WorkflowCore.DSL/Models}/v1/StepSourceV1.cs (100%) create mode 100644 src/WorkflowCore.DSL/ServiceCollectionExtensions.cs rename src/{WorkflowCore/Services/DefinitionStorage => WorkflowCore.DSL/Services}/DefinitionLoader.cs (100%) rename src/{WorkflowCore/Services/DefinitionStorage => WorkflowCore.DSL/Services}/Deserializers.cs (100%) create mode 100644 src/WorkflowCore.DSL/WorkflowCore.DSL.csproj diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 612f99bcd..1b561cda8 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2027 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29509.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF47161E-E399-451C-BDE8-E92AAD3BD761}" EndProject @@ -135,6 +135,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.Elastics EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.Redis", "test\WorkflowCore.Tests.Redis\WorkflowCore.Tests.Redis.csproj", "{78217204-B873-40B9-8875-E3925B2FBCEC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.DSL", "src\WorkflowCore.DSL\WorkflowCore.DSL.csproj", "{20B98905-08CB-4854-8E2C-A31A078383E9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -329,6 +331,10 @@ Global {78217204-B873-40B9-8875-E3925B2FBCEC}.Debug|Any CPU.Build.0 = Debug|Any CPU {78217204-B873-40B9-8875-E3925B2FBCEC}.Release|Any CPU.ActiveCfg = Release|Any CPU {78217204-B873-40B9-8875-E3925B2FBCEC}.Release|Any CPU.Build.0 = Release|Any CPU + {20B98905-08CB-4854-8E2C-A31A078383E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20B98905-08CB-4854-8E2C-A31A078383E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20B98905-08CB-4854-8E2C-A31A078383E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20B98905-08CB-4854-8E2C-A31A078383E9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -384,6 +390,7 @@ Global {F6348170-B695-4D97-BAE6-4F0F643F3BEF} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} {44644716-0CE8-4837-B189-AB65AE2106AA} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {78217204-B873-40B9-8875-E3925B2FBCEC} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} + {20B98905-08CB-4854-8E2C-A31A078383E9} = {EF47161E-E399-451C-BDE8-E92AAD3BD761} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/docs/json-yaml.md b/docs/json-yaml.md index 305741ec6..6b73358a4 100644 --- a/docs/json-yaml.md +++ b/docs/json-yaml.md @@ -1,6 +1,7 @@ # Loading workflow definitions from JSON or YAML -Simply grab the `DefinitionLoader` from the IoC container and call the `.LoadDefinition` method +Install the `WorkflowCore.DSL` package from nuget and call `AddWorkflowDSL` on your service collection. +Then grab the `DefinitionLoader` from the IoC container and call the `.LoadDefinition` method ```c# using WorkflowCore.Interface; diff --git a/src/WorkflowCore/Interface/IDefinitionLoader.cs b/src/WorkflowCore.DSL/Interface/IDefinitionLoader.cs similarity index 100% rename from src/WorkflowCore/Interface/IDefinitionLoader.cs rename to src/WorkflowCore.DSL/Interface/IDefinitionLoader.cs diff --git a/src/WorkflowCore/Models/DefinitionStorage/DefinitionSource.cs b/src/WorkflowCore.DSL/Models/DefinitionSource.cs similarity index 100% rename from src/WorkflowCore/Models/DefinitionStorage/DefinitionSource.cs rename to src/WorkflowCore.DSL/Models/DefinitionSource.cs diff --git a/src/WorkflowCore/Models/DefinitionStorage/Envelope.cs b/src/WorkflowCore.DSL/Models/Envelope.cs similarity index 100% rename from src/WorkflowCore/Models/DefinitionStorage/Envelope.cs rename to src/WorkflowCore.DSL/Models/Envelope.cs diff --git a/src/WorkflowCore/Models/DefinitionStorage/v1/DefinitionSourceV1.cs b/src/WorkflowCore.DSL/Models/v1/DefinitionSourceV1.cs similarity index 100% rename from src/WorkflowCore/Models/DefinitionStorage/v1/DefinitionSourceV1.cs rename to src/WorkflowCore.DSL/Models/v1/DefinitionSourceV1.cs diff --git a/src/WorkflowCore/Models/DefinitionStorage/v1/MappingSourceV1.cs b/src/WorkflowCore.DSL/Models/v1/MappingSourceV1.cs similarity index 100% rename from src/WorkflowCore/Models/DefinitionStorage/v1/MappingSourceV1.cs rename to src/WorkflowCore.DSL/Models/v1/MappingSourceV1.cs diff --git a/src/WorkflowCore/Models/DefinitionStorage/v1/StepSourceV1.cs b/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs similarity index 100% rename from src/WorkflowCore/Models/DefinitionStorage/v1/StepSourceV1.cs rename to src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs diff --git a/src/WorkflowCore.DSL/ServiceCollectionExtensions.cs b/src/WorkflowCore.DSL/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..113236e91 --- /dev/null +++ b/src/WorkflowCore.DSL/ServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.Interface; +using WorkflowCore.Services.DefinitionStorage; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddWorkflowDSL(this IServiceCollection services) + { + services.AddTransient(); + return services; + } + } +} + diff --git a/src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs similarity index 100% rename from src/WorkflowCore/Services/DefinitionStorage/DefinitionLoader.cs rename to src/WorkflowCore.DSL/Services/DefinitionLoader.cs diff --git a/src/WorkflowCore/Services/DefinitionStorage/Deserializers.cs b/src/WorkflowCore.DSL/Services/Deserializers.cs similarity index 100% rename from src/WorkflowCore/Services/DefinitionStorage/Deserializers.cs rename to src/WorkflowCore.DSL/Services/Deserializers.cs diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj new file mode 100644 index 000000000..4a27cf7ea --- /dev/null +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -0,0 +1,24 @@ + + + + netstandard2.0 + 3.0.0 + DSL extenstion for Workflow Core provding support for JSON and YAML workflow definitions. + Daniel Gerlag + + WorkflowCore + https://github.com/danielgerlag/workflow-core + https://github.com/danielgerlag/workflow-core.git + git + + + + + + + + + + + + diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 75e40299b..63bef85f7 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -10,7 +10,6 @@ using Microsoft.Extensions.ObjectPool; using WorkflowCore.Primitives; using WorkflowCore.Services.BackgroundTasks; -using WorkflowCore.Services.DefinitionStorage; using WorkflowCore.Services.ErrorHandlers; namespace Microsoft.Extensions.DependencyInjection @@ -60,7 +59,6 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddTransient, InjectedObjectPoolPolicy>(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index c191d7bfe..96b49545a 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -38,4 +38,8 @@ + + + + diff --git a/test/WorkflowCore.Testing/JsonWorkflowTest.cs b/test/WorkflowCore.Testing/JsonWorkflowTest.cs index 78f36eae8..cd66550a7 100644 --- a/test/WorkflowCore.Testing/JsonWorkflowTest.cs +++ b/test/WorkflowCore.Testing/JsonWorkflowTest.cs @@ -52,6 +52,7 @@ private void Host_OnStepError(WorkflowInstance workflow, WorkflowStep step, Exce protected virtual void ConfigureServices(IServiceCollection services) { services.AddWorkflow(); + services.AddWorkflowDSL(); } public string StartWorkflow(string json, object data) diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 968a41116..c0412a10a 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -15,6 +15,7 @@ + diff --git a/test/WorkflowCore.Testing/YamlWorkflowTest.cs b/test/WorkflowCore.Testing/YamlWorkflowTest.cs index 6bd524e60..def1e0353 100644 --- a/test/WorkflowCore.Testing/YamlWorkflowTest.cs +++ b/test/WorkflowCore.Testing/YamlWorkflowTest.cs @@ -52,6 +52,7 @@ private void Host_OnStepError(WorkflowInstance workflow, WorkflowStep step, Exce protected virtual void ConfigureServices(IServiceCollection services) { services.AddWorkflow(); + services.AddWorkflowDSL(); } public string StartWorkflow(string json, object data) diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index 95765e41a..294adbd23 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -11,6 +11,7 @@ + From d2d3796d3347d5d9cbc86969b2c91111d78157b7 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 17 Dec 2019 20:19:06 -0800 Subject: [PATCH 176/462] activity workers --- README.md | 2 + WorkflowCore.sln | 7 + docs/activities.md | 50 +++ docs/external-events.md | 1 + docs/json-yaml.md | 39 +- docs/samples.md | 2 + mkdocs.yml | 1 + .../Exceptions/ActivityFailedException.cs | 12 + .../Interface/IActivityController.cs | 25 ++ .../Interface/IPersistenceProvider.cs | 50 --- src/WorkflowCore/Interface/IStepBuilder.cs | 10 + src/WorkflowCore/Interface/IWorkflowHost.cs | 2 +- .../Interface/Persistence/IEventRepository.cs | 23 ++ .../Persistence/IPersistenceProvider.cs | 16 + .../Persistence/ISubscriptionRepository.cs | 25 ++ .../Persistence/IWorkflowRepository.cs | 24 ++ src/WorkflowCore/Models/ActivityResult.cs | 14 + src/WorkflowCore/Models/Event.cs | 2 + src/WorkflowCore/Models/EventSubscription.cs | 8 + src/WorkflowCore/Models/ExecutionResult.cs | 14 + src/WorkflowCore/Primitives/Activity.cs | 52 +++ .../ServiceCollectionExtensions.cs | 4 + .../Services/ActivityController.cs | 155 ++++++++ .../Services/BackgroundTasks/EventConsumer.cs | 26 +- .../MemoryPersistenceProvider.cs | 49 ++- .../TransientMemoryPersistenceProvider.cs | 9 +- .../Services/ExecutionResultProcessor.cs | 4 +- .../Services/FluentBuilders/StepBuilder.cs | 19 + src/WorkflowCore/Services/WorkflowHost.cs | 24 +- src/WorkflowCore/WorkflowCore.csproj | 5 +- .../ExtensionMethods.cs | 10 +- .../Models/PersistedSubscription.cs | 11 + .../EntityFrameworkPersistenceProvider.cs | 61 +++- ...lowCore.Persistence.EntityFramework.csproj | 5 +- .../Services/MongoPersistenceProvider.cs | 41 ++- .../WorkflowCore.Persistence.MongoDB.csproj | 5 +- .../20191221210630_Activities.Designer.cs | 324 +++++++++++++++++ .../Migrations/20191221210630_Activities.cs | 170 +++++++++ ...ostgresPersistenceProviderModelSnapshot.cs | 163 ++++++--- ...WorkflowCore.Persistence.PostgreSQL.csproj | 5 +- .../20191221210710_Activities.Designer.cs | 336 ++++++++++++++++++ .../Migrations/20191221210710_Activities.cs | 61 ++++ ...lServerPersistenceProviderModelSnapshot.cs | 155 ++++++-- .../WorkflowCore.Persistence.SqlServer.csproj | 5 +- .../WorkflowCore.Persistence.Sqlite.csproj | 7 +- .../ModelExtensions.cs | 29 +- .../Services/DynamoDbProvisioner.cs | 2 +- .../Services/DynamoPersistenceProvider.cs | 102 +++++- .../WorkflowCore.Providers.AWS.csproj | 3 +- .../Services/RedisPersistenceProvider.cs | 38 +- .../WorkflowCore.Providers.Redis.csproj | 1 + .../WorkflowCore.Sample04.csproj | 4 + .../WorkflowCore.Sample18/ActivityWorkflow.cs | 32 ++ src/samples/WorkflowCore.Sample18/Program.cs | 55 +++ src/samples/WorkflowCore.Sample18/README.md | 28 ++ .../Steps/CustomMessage.cs | 21 ++ .../Steps/GoodbyeWorld.cs | 18 + .../WorkflowCore.Sample18/Steps/HelloWorld.cs | 18 + .../WorkflowCore.Sample18.csproj | 20 ++ test/ScratchPad/ElasticTest.cs | 81 +++++ test/ScratchPad/HelloWorld.json | 29 +- test/ScratchPad/Program.cs | 99 ++++-- test/ScratchPad/ScratchPad.csproj | 9 +- .../Scenarios/ActivityScenario.cs | 76 ++++ test/WorkflowCore.Testing/JsonWorkflowTest.cs | 2 +- test/WorkflowCore.Testing/WorkflowTest.cs | 2 +- test/WorkflowCore.Testing/YamlWorkflowTest.cs | 2 +- .../Scenarios/MongoActivityScenario.cs | 22 ++ .../Scenarios/PostgresActivityScenario.cs | 18 + .../Scenarios/SqlServerActivityScenario.cs | 18 + 70 files changed, 2541 insertions(+), 221 deletions(-) create mode 100644 docs/activities.md create mode 100644 src/WorkflowCore/Exceptions/ActivityFailedException.cs create mode 100644 src/WorkflowCore/Interface/IActivityController.cs delete mode 100644 src/WorkflowCore/Interface/IPersistenceProvider.cs create mode 100644 src/WorkflowCore/Interface/Persistence/IEventRepository.cs create mode 100644 src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs create mode 100644 src/WorkflowCore/Interface/Persistence/ISubscriptionRepository.cs create mode 100644 src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs create mode 100644 src/WorkflowCore/Models/ActivityResult.cs create mode 100644 src/WorkflowCore/Primitives/Activity.cs create mode 100644 src/WorkflowCore/Services/ActivityController.cs create mode 100644 src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.cs create mode 100644 src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191221210710_Activities.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191221210710_Activities.cs create mode 100644 src/samples/WorkflowCore.Sample18/ActivityWorkflow.cs create mode 100644 src/samples/WorkflowCore.Sample18/Program.cs create mode 100644 src/samples/WorkflowCore.Sample18/README.md create mode 100644 src/samples/WorkflowCore.Sample18/Steps/CustomMessage.cs create mode 100644 src/samples/WorkflowCore.Sample18/Steps/GoodbyeWorld.cs create mode 100644 src/samples/WorkflowCore.Sample18/Steps/HelloWorld.cs create mode 100644 src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj create mode 100644 test/ScratchPad/ElasticTest.cs create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs create mode 100644 test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs create mode 100644 test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresActivityScenario.cs create mode 100644 test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerActivityScenario.cs diff --git a/README.md b/README.md index e54f50589..7faad2599 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,8 @@ These are also available as separate Nuget packages. * [Events](src/samples/WorkflowCore.Sample04) +* [Activity Workers](src/samples/WorkflowCore.Sample18) + * [Parallel Tasks](src/samples/WorkflowCore.Sample13) * [Saga Transactions (with compensation)](src/samples/WorkflowCore.Sample17) diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 1b561cda8..8177c2ef9 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -137,6 +137,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.Redis", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.DSL", "src\WorkflowCore.DSL\WorkflowCore.DSL.csproj", "{20B98905-08CB-4854-8E2C-A31A078383E9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample18", "src\samples\WorkflowCore.Sample18\WorkflowCore.Sample18.csproj", "{5BE6D628-B9DB-4C76-AAEB-8F3800509A84}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -335,6 +337,10 @@ Global {20B98905-08CB-4854-8E2C-A31A078383E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {20B98905-08CB-4854-8E2C-A31A078383E9}.Release|Any CPU.ActiveCfg = Release|Any CPU {20B98905-08CB-4854-8E2C-A31A078383E9}.Release|Any CPU.Build.0 = Release|Any CPU + {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -391,6 +397,7 @@ Global {44644716-0CE8-4837-B189-AB65AE2106AA} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {78217204-B873-40B9-8875-E3925B2FBCEC} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {20B98905-08CB-4854-8E2C-A31A078383E9} = {EF47161E-E399-451C-BDE8-E92AAD3BD761} + {5BE6D628-B9DB-4C76-AAEB-8F3800509A84} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/docs/activities.md b/docs/activities.md new file mode 100644 index 000000000..d458d81f2 --- /dev/null +++ b/docs/activities.md @@ -0,0 +1,50 @@ +# Activities + +An activity is defined as an item on an external queue of work, that a workflow can wait for. + +In this example the workflow will wait for `activity-1`, before proceeding. It also passes the value of `data.Value1` to the activity, it then maps the result of the activity to `data.Value2`. + +Then we create a worker to process the queue of activity items. It uses the `GetPendingActivity` method to get an activity and the data that a workflow is waiting for. + + + +```C# +public class ActivityWorkflow : IWorkflow +{ + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Activity("activity-1", (data) => data.Value1) + .Output(data => data.Value2, step => step.Result) + .Then() + .Input(step => step.Message, data => data.Value2); + } + +} +... + +var activity = host.GetPendingActivity("activity-1", "worker1", TimeSpan.FromMinutes(1)).Result; + +if (activity != null) +{ + Console.WriteLine(activity.Parameters); + host.SubmitActivitySuccess(activity.Token, "Some response data"); +} + +``` + +The JSON representation of this step would look like this + +```json +{ + "Id": "activity-step", + "StepType": "WorkflowCore.Primitives.Activity, WorkflowCore", + "Inputs": + { + "ActivityName": "\"activity-1\"", + "Parameters": "data.Value1" + }, + "Outputs": { "Value2": "step.Result" } +} +``` \ No newline at end of file diff --git a/docs/external-events.md b/docs/external-events.md index 3ed3b3cf4..9a4214527 100644 --- a/docs/external-events.md +++ b/docs/external-events.md @@ -24,3 +24,4 @@ host.PublishEvent("MyEvent", "0", "hello"); ## Effective Date You can also specify an effective date when waiting for events, which allows you to respond to events that may have already occurred in the past, or only ones that occur after the effective date. + diff --git a/docs/json-yaml.md b/docs/json-yaml.md index 6b73358a4..94f9767df 100644 --- a/docs/json-yaml.md +++ b/docs/json-yaml.md @@ -149,7 +149,7 @@ Steps: ### WaitFor -The `.WaitFor` can be implemented using 3 inputs as follows +The `.WaitFor` can be implemented using inputs as follows | Field | Description | | ---------------------- | --------------------------- | @@ -184,6 +184,43 @@ Inputs: ``` +### Activity + +The `.Activity` can be implemented using inputs as follows + +| Field | Description | +| ---------------------- | --------------------------- | +| CancelCondition | Optional expression to specify a cancel condition | +| Inputs.ActivityName | Expression to specify the activity name | +| Inputs.Parameters | Expression to specify the parameters to pass the activity worker | +| Inputs.EffectiveDate | Optional expression to specify the effective date | + + +```json +{ + "Id": "MyActivityStep", + "StepType": "WorkflowCore.Primitives.Activity, WorkflowCore", + "NextStepId": "...", + "CancelCondition": "...", + "Inputs": { + "ActivityName": "\"my-activity\"", + "Parameters": "data.SomeValue" + } +} +``` +```yaml +Id: MyActivityStep +StepType: WorkflowCore.Primitives.Activity, WorkflowCore +NextStepId: "..." +CancelCondition: "..." +Inputs: + ActivityName: '"my-activity"' + EventKey: '"Key1"' + Parameters: data.SomeValue + +``` + + ### If The `.If` can be implemented as follows diff --git a/docs/samples.md b/docs/samples.md index 4afa753d3..a1b862799 100644 --- a/docs/samples.md +++ b/docs/samples.md @@ -6,6 +6,8 @@ [Events](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample04) +[Activity Workers](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample18) + [Dependency Injection](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample15) [Parallel ForEach](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample09) diff --git a/mkdocs.yml b/mkdocs.yml index 1e2469827..1081d0f05 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,6 +3,7 @@ nav: - Home: index.md - Getting started: getting-started.md - External events: external-events.md + - Activity workers: activities.md - Error handing: error-handling.md - Control structures: control-structures.md - Saga transactions: sagas.md diff --git a/src/WorkflowCore/Exceptions/ActivityFailedException.cs b/src/WorkflowCore/Exceptions/ActivityFailedException.cs new file mode 100644 index 000000000..4cc22efa2 --- /dev/null +++ b/src/WorkflowCore/Exceptions/ActivityFailedException.cs @@ -0,0 +1,12 @@ +using System; + +namespace WorkflowCore.Exceptions +{ + public class ActivityFailedException : Exception + { + public ActivityFailedException(object data) + { + // + } + } +} \ No newline at end of file diff --git a/src/WorkflowCore/Interface/IActivityController.cs b/src/WorkflowCore/Interface/IActivityController.cs new file mode 100644 index 000000000..99e5c1737 --- /dev/null +++ b/src/WorkflowCore/Interface/IActivityController.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace WorkflowCore.Interface +{ + public class PendingActivity + { + public string Token { get; set; } + public string ActivityName { get; set; } + public object Parameters { get; set; } + public DateTime TokenExpiry { get; set; } + + } + + public interface IActivityController + { + Task GetPendingActivity(string activityName, string workerId, TimeSpan? timeout = null); + Task ReleaseActivityToken(string token); + Task SubmitActivitySuccess(string token, object result); + Task SubmitActivityFailure(string token, object result); + + } +} diff --git a/src/WorkflowCore/Interface/IPersistenceProvider.cs b/src/WorkflowCore/Interface/IPersistenceProvider.cs deleted file mode 100644 index 2c2d707ef..000000000 --- a/src/WorkflowCore/Interface/IPersistenceProvider.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using WorkflowCore.Models; - -namespace WorkflowCore.Interface -{ - /// - /// The implemention of this interface will be responsible for - /// persisting running workflow instances to a durable store - /// - public interface IPersistenceProvider - { - Task CreateNewWorkflow(WorkflowInstance workflow); - - Task PersistWorkflow(WorkflowInstance workflow); - - Task> GetRunnableInstances(DateTime asAt); - - [Obsolete] - Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take); - - Task GetWorkflowInstance(string Id); - - Task> GetWorkflowInstances(IEnumerable ids); - - Task CreateEventSubscription(EventSubscription subscription); - - Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf); - - Task TerminateSubscription(string eventSubscriptionId); - - Task CreateEvent(Event newEvent); - - Task GetEvent(string id); - - Task> GetRunnableEvents(DateTime asAt); - - Task> GetEvents(string eventName, string eventKey, DateTime asOf); - - Task MarkEventProcessed(string id); - - Task MarkEventUnprocessed(string id); - - Task PersistErrors(IEnumerable errors); - - void EnsureStoreExists(); - - } -} diff --git a/src/WorkflowCore/Interface/IStepBuilder.cs b/src/WorkflowCore/Interface/IStepBuilder.cs index e9bf65817..e87cfe438 100644 --- a/src/WorkflowCore/Interface/IStepBuilder.cs +++ b/src/WorkflowCore/Interface/IStepBuilder.cs @@ -250,5 +250,15 @@ public interface IStepBuilder /// IStepBuilder CancelCondition(Expression> cancelCondition, bool proceedAfterCancel = false); + /// + /// Wait here until an external activity is complete + /// + /// The name used to identify the activity to wait for + /// The data to pass the external activity worker + /// Listen for events as of this effective date + /// A conditon that when true will cancel this WaitFor + /// + IStepBuilder Activity(string activityName, Expression> parameters = null, Expression> effectiveDate = null, Expression> cancelCondition = null); + } } diff --git a/src/WorkflowCore/Interface/IWorkflowHost.cs b/src/WorkflowCore/Interface/IWorkflowHost.cs index 0786b3b78..8e279096c 100644 --- a/src/WorkflowCore/Interface/IWorkflowHost.cs +++ b/src/WorkflowCore/Interface/IWorkflowHost.cs @@ -6,7 +6,7 @@ namespace WorkflowCore.Interface { - public interface IWorkflowHost : IWorkflowController, IHostedService + public interface IWorkflowHost : IWorkflowController, IActivityController, IHostedService { /// /// Start the workflow host, this enable execution of workflows diff --git a/src/WorkflowCore/Interface/Persistence/IEventRepository.cs b/src/WorkflowCore/Interface/Persistence/IEventRepository.cs new file mode 100644 index 000000000..63abdd43c --- /dev/null +++ b/src/WorkflowCore/Interface/Persistence/IEventRepository.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface IEventRepository + { + Task CreateEvent(Event newEvent); + + Task GetEvent(string id); + + Task> GetRunnableEvents(DateTime asAt); + + Task> GetEvents(string eventName, string eventKey, DateTime asOf); + + Task MarkEventProcessed(string id); + + Task MarkEventUnprocessed(string id); + + } +} diff --git a/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs b/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs new file mode 100644 index 000000000..165dc7cbb --- /dev/null +++ b/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface IPersistenceProvider : IWorkflowRepository, ISubscriptionRepository, IEventRepository + { + + Task PersistErrors(IEnumerable errors); + + void EnsureStoreExists(); + + } +} diff --git a/src/WorkflowCore/Interface/Persistence/ISubscriptionRepository.cs b/src/WorkflowCore/Interface/Persistence/ISubscriptionRepository.cs new file mode 100644 index 000000000..c37cf2f32 --- /dev/null +++ b/src/WorkflowCore/Interface/Persistence/ISubscriptionRepository.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface ISubscriptionRepository + { + Task CreateEventSubscription(EventSubscription subscription); + + Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf); + + Task TerminateSubscription(string eventSubscriptionId); + + Task GetSubscription(string eventSubscriptionId); + + Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf); + + Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry); + + Task ClearSubscriptionToken(string eventSubscriptionId, string token); + + } +} diff --git a/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs b/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs new file mode 100644 index 000000000..333423561 --- /dev/null +++ b/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface IWorkflowRepository + { + Task CreateNewWorkflow(WorkflowInstance workflow); + + Task PersistWorkflow(WorkflowInstance workflow); + + Task> GetRunnableInstances(DateTime asAt); + + [Obsolete] + Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take); + + Task GetWorkflowInstance(string Id); + + Task> GetWorkflowInstances(IEnumerable ids); + + } +} diff --git a/src/WorkflowCore/Models/ActivityResult.cs b/src/WorkflowCore/Models/ActivityResult.cs new file mode 100644 index 000000000..940c07393 --- /dev/null +++ b/src/WorkflowCore/Models/ActivityResult.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models +{ + + public class ActivityResult + { + public enum StatusType { Success, Fail } + public StatusType Status { get; set; } + public object Data { get; set; } + } +} diff --git a/src/WorkflowCore/Models/Event.cs b/src/WorkflowCore/Models/Event.cs index 42dff60d6..5db6b4987 100644 --- a/src/WorkflowCore/Models/Event.cs +++ b/src/WorkflowCore/Models/Event.cs @@ -15,5 +15,7 @@ public class Event public DateTime EventTime { get; set; } public bool IsProcessed { get; set; } + + public const string EventTypeActivity = "WorkflowCore.Activity"; } } diff --git a/src/WorkflowCore/Models/EventSubscription.cs b/src/WorkflowCore/Models/EventSubscription.cs index 3f7c3c737..a5661987b 100644 --- a/src/WorkflowCore/Models/EventSubscription.cs +++ b/src/WorkflowCore/Models/EventSubscription.cs @@ -10,6 +10,8 @@ public class EventSubscription public int StepId { get; set; } + public string ExecutionPointerId { get; set; } + public string EventName { get; set; } public string EventKey { get; set; } @@ -17,5 +19,11 @@ public class EventSubscription public DateTime SubscribeAsOf { get; set; } public object SubscriptionData { get; set; } + + public string ExternalToken { get; set; } + + public string ExternalWorkerId { get; set; } + + public DateTime? ExternalTokenExpiry { get; set; } } } diff --git a/src/WorkflowCore/Models/ExecutionResult.cs b/src/WorkflowCore/Models/ExecutionResult.cs index 9d6e791f6..54c2c55f2 100644 --- a/src/WorkflowCore/Models/ExecutionResult.cs +++ b/src/WorkflowCore/Models/ExecutionResult.cs @@ -18,6 +18,8 @@ public class ExecutionResult public string EventKey { get; set; } public DateTime EventAsOf { get; set; } + + public object SubscriptionData { get; set; } public List BranchValues { get; set; } = new List(); @@ -88,5 +90,17 @@ public static ExecutionResult WaitForEvent(string eventName, string eventKey, Da EventAsOf = effectiveDate.ToUniversalTime() }; } + + public static ExecutionResult WaitForActivity(string activityName, object subscriptionData, DateTime effectiveDate) + { + return new ExecutionResult() + { + Proceed = false, + EventName = Event.EventTypeActivity, + EventKey = activityName, + SubscriptionData = subscriptionData, + EventAsOf = effectiveDate.ToUniversalTime() + }; + } } } diff --git a/src/WorkflowCore/Primitives/Activity.cs b/src/WorkflowCore/Primitives/Activity.cs new file mode 100644 index 000000000..4f0ebb0c1 --- /dev/null +++ b/src/WorkflowCore/Primitives/Activity.cs @@ -0,0 +1,52 @@ +using System; +using WorkflowCore.Exceptions; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Primitives +{ + public class Activity : StepBody + { + public string ActivityName { get; set; } + + public DateTime EffectiveDate { get; set; } + + public object Parameters { get; set; } + + public object Result { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + if (!context.ExecutionPointer.EventPublished) + { + DateTime effectiveDate = DateTime.MinValue; + + if (EffectiveDate != null) + { + effectiveDate = EffectiveDate; + } + + return ExecutionResult.WaitForActivity(ActivityName, Parameters, effectiveDate); + } + + if (context.ExecutionPointer.EventData is ActivityResult) + { + var actResult = (context.ExecutionPointer.EventData as ActivityResult); + if (actResult.Status == ActivityResult.StatusType.Success) + { + Result = actResult.Data; + } + else + { + throw new ActivityFailedException(actResult.Data); + } + } + else + { + Result = context.ExecutionPointer.EventData; + } + + return ExecutionResult.Next(); + } + } +} diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 63bef85f7..c241107c2 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -25,6 +25,9 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A setupAction?.Invoke(options); services.AddSingleton(); services.AddTransient(options.PersistanceFactory); + services.AddTransient(options.PersistanceFactory); + services.AddTransient(options.PersistanceFactory); + services.AddTransient(options.PersistanceFactory); services.AddSingleton(options.QueueFactory); services.AddSingleton(options.LockFactory); services.AddSingleton(options.EventHubFactory); @@ -46,6 +49,7 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddTransient(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddTransient(); services.AddTransient(); diff --git a/src/WorkflowCore/Services/ActivityController.cs b/src/WorkflowCore/Services/ActivityController.cs new file mode 100644 index 000000000..b832ce596 --- /dev/null +++ b/src/WorkflowCore/Services/ActivityController.cs @@ -0,0 +1,155 @@ +using System; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Services +{ + public class ActivityController : IActivityController + { + private readonly IWorkflowRepository _persistenceStore; + private readonly ISubscriptionRepository _subscriptionRepository; + private readonly IDistributedLockProvider _lockProvider; + private readonly IDateTimeProvider _dateTimeProvider; + private readonly IQueueProvider _queueProvider; + + public ActivityController(ISubscriptionRepository subscriptionRepository, IWorkflowRepository persistenceStore, IDateTimeProvider dateTimeProvider, IDistributedLockProvider lockProvider, IQueueProvider queueProvider) + { + _persistenceStore = persistenceStore; + _subscriptionRepository = subscriptionRepository; + _dateTimeProvider = dateTimeProvider; + _lockProvider = lockProvider; + _queueProvider = queueProvider; + } + + public async Task GetPendingActivity(string activityName, string workerId, TimeSpan? timeout = null) + { + var endTime = DateTime.UtcNow.Add(timeout ?? TimeSpan.Zero); + var firstPass = true; + EventSubscription subscription = null; + while ((subscription == null && DateTime.UtcNow < endTime) || firstPass) + { + if (!firstPass) + await Task.Delay(100); + subscription = await _subscriptionRepository.GetFirstOpenSubscription(Event.EventTypeActivity, activityName, _dateTimeProvider.Now); + if (subscription != null) + if (!await _lockProvider.AcquireLock($"sub:{subscription.Id}", CancellationToken.None)) + subscription = null; + firstPass = false; + } + if (subscription == null) + return null; + + try + { + var token = Token.Create(subscription.Id, subscription.EventKey); + var result = new PendingActivity() + { + Token = token.Encode(), + ActivityName = subscription.EventKey, + Parameters = subscription.SubscriptionData, + TokenExpiry = DateTime.MaxValue + }; + + if (!await _subscriptionRepository.SetSubscriptionToken(subscription.Id, result.Token, workerId, result.TokenExpiry)) + return null; + + return result; + } + finally + { + await _lockProvider.ReleaseLock($"sub:{subscription.Id}"); + } + + } + + public async Task ReleaseActivityToken(string token) + { + var tokenObj = Token.Decode(token); + await _subscriptionRepository.ClearSubscriptionToken(tokenObj.SubscriptionId, token); + } + + public async Task SubmitActivitySuccess(string token, object result) + { + await SubmitActivityResult(token, new ActivityResult() + { + Data = result, + Status = ActivityResult.StatusType.Success + }); + } + + public async Task SubmitActivityFailure(string token, object result) + { + await SubmitActivityResult(token, new ActivityResult() + { + Data = result, + Status = ActivityResult.StatusType.Fail + }); + } + + private async Task SubmitActivityResult(string token, object result) + { + var tokenObj = Token.Decode(token); + var sub = await _subscriptionRepository.GetSubscription(tokenObj.SubscriptionId); + if (sub.ExternalToken != token) + throw new InvalidOperationException("Token mismatch"); + + if (!await _lockProvider.AcquireLock(sub.WorkflowId, CancellationToken.None)) + throw new InvalidOperationException("Workflow is locked"); + + try + { + var workflow = await _persistenceStore.GetWorkflowInstance(sub.WorkflowId); + var pointer = workflow.ExecutionPointers.Single(p => p.Id == sub.ExecutionPointerId); + + pointer.EventData = result; + pointer.EventPublished = true; + pointer.Active = true; + + workflow.NextExecution = 0; + await _persistenceStore.PersistWorkflow(workflow); + await _subscriptionRepository.TerminateSubscription(sub.Id); + } + finally + { + await _lockProvider.ReleaseLock(sub.WorkflowId); + await _queueProvider.QueueWork(sub.WorkflowId, QueueType.Workflow); + } + } + + class Token + { + public string SubscriptionId { get; set; } + public string ActivityName { get; set; } + public string Nonce { get; set; } + + public string Encode() + { + var json = JsonConvert.SerializeObject(this); + return Convert.ToBase64String(Encoding.UTF8.GetBytes(json)); + } + + public static Token Create(string subscriptionId, string activityName) + { + return new Token() + { + SubscriptionId = subscriptionId, + ActivityName = activityName, + Nonce = Guid.NewGuid().ToString() + }; + } + + public static Token Decode(string encodedToken) + { + var raw = Convert.FromBase64String(encodedToken); + var json = Encoding.UTF8.GetString(raw); + return JsonConvert.DeserializeObject(json); + } + } + } +} \ No newline at end of file diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index 0fa13ef84..ed25fa9a4 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -11,16 +11,20 @@ namespace WorkflowCore.Services.BackgroundTasks { internal class EventConsumer : QueueConsumer, IBackgroundTask { - private readonly IPersistenceProvider _persistenceStore; + private readonly IWorkflowRepository _workflowRepository; + private readonly ISubscriptionRepository _subscriptionRepository; + private readonly IEventRepository _eventRepository; private readonly IDistributedLockProvider _lockProvider; private readonly IDateTimeProvider _datetimeProvider; protected override int MaxConcurrentItems => 2; protected override QueueType Queue => QueueType.Event; - public EventConsumer(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, WorkflowOptions options, IDateTimeProvider datetimeProvider) + public EventConsumer(IWorkflowRepository workflowRepository, ISubscriptionRepository subscriptionRepository, IEventRepository eventRepository, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, WorkflowOptions options, IDateTimeProvider datetimeProvider) : base(queueProvider, loggerFactory, options) { - _persistenceStore = persistenceStore; + _workflowRepository = workflowRepository; + _subscriptionRepository = subscriptionRepository; + _eventRepository = eventRepository; _lockProvider = lockProvider; _datetimeProvider = datetimeProvider; } @@ -36,10 +40,10 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance try { cancellationToken.ThrowIfCancellationRequested(); - var evt = await _persistenceStore.GetEvent(itemId); + var evt = await _eventRepository.GetEvent(itemId); if (evt.EventTime <= _datetimeProvider.UtcNow) { - var subs = await _persistenceStore.GetSubcriptions(evt.EventName, evt.EventKey, evt.EventTime); + var subs = await _subscriptionRepository.GetSubscriptions(evt.EventName, evt.EventKey, evt.EventTime); var toQueue = new List(); var complete = true; @@ -47,7 +51,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance complete = complete && await SeedSubscription(evt, sub, toQueue, cancellationToken); if (complete) - await _persistenceStore.MarkEventProcessed(itemId); + await _eventRepository.MarkEventProcessed(itemId); foreach (var eventId in toQueue) await QueueProvider.QueueWork(eventId, QueueType.Event); @@ -61,12 +65,12 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance private async Task SeedSubscription(Event evt, EventSubscription sub, List toQueue, CancellationToken cancellationToken) { - foreach (var eventId in await _persistenceStore.GetEvents(sub.EventName, sub.EventKey, sub.SubscribeAsOf)) + foreach (var eventId in await _eventRepository.GetEvents(sub.EventName, sub.EventKey, sub.SubscribeAsOf)) { if (eventId == evt.Id) continue; - var siblingEvent = await _persistenceStore.GetEvent(eventId); + var siblingEvent = await _eventRepository.GetEvent(eventId); if ((!siblingEvent.IsProcessed) && (siblingEvent.EventTime < evt.EventTime)) { await QueueProvider.QueueWork(eventId, QueueType.Event); @@ -85,7 +89,7 @@ private async Task SeedSubscription(Event evt, EventSubscription sub, List try { - var workflow = await _persistenceStore.GetWorkflowInstance(sub.WorkflowId); + var workflow = await _workflowRepository.GetWorkflowInstance(sub.WorkflowId); var pointers = workflow.ExecutionPointers.Where(p => p.EventName == sub.EventName && p.EventKey == sub.EventKey && !p.EventPublished && p.EndTime == null); foreach (var p in pointers) { @@ -94,8 +98,8 @@ private async Task SeedSubscription(Event evt, EventSubscription sub, List p.Active = true; } workflow.NextExecution = 0; - await _persistenceStore.PersistWorkflow(workflow); - await _persistenceStore.TerminateSubscription(sub.Id); + await _workflowRepository.PersistWorkflow(workflow); + await _subscriptionRepository.TerminateSubscription(sub.Id); return true; } catch (Exception ex) diff --git a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs index d69984506..fd27db8ca 100644 --- a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs @@ -115,7 +115,7 @@ public async Task CreateEventSubscription(EventSubscription subscription } } - public async Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) { lock (_subscriptions) { @@ -133,6 +133,53 @@ public async Task TerminateSubscription(string eventSubscriptionId) } } + public Task GetSubscription(string eventSubscriptionId) + { + lock (_subscriptions) + { + var sub = _subscriptions.Single(x => x.Id == eventSubscriptionId); + return Task.FromResult(sub); + } + } + + public Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + { + lock (_subscriptions) + { + var result = _subscriptions + .FirstOrDefault(x => x.ExternalToken == null && x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf); + return Task.FromResult(result); + } + } + + public Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + { + lock (_subscriptions) + { + var sub = _subscriptions.Single(x => x.Id == eventSubscriptionId); + sub.ExternalToken = token; + sub.ExternalWorkerId = workerId; + sub.ExternalTokenExpiry = expiry; + + return Task.FromResult(true); + } + } + + public Task ClearSubscriptionToken(string eventSubscriptionId, string token) + { + lock (_subscriptions) + { + var sub = _subscriptions.Single(x => x.Id == eventSubscriptionId); + if (sub.ExternalToken != token) + throw new InvalidOperationException(); + sub.ExternalToken = null; + sub.ExternalWorkerId = null; + sub.ExternalTokenExpiry = null; + + return Task.CompletedTask; + } + } + public void EnsureStoreExists() { } diff --git a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs index 599119557..6b3084a89 100644 --- a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs @@ -32,7 +32,7 @@ public TransientMemoryPersistenceProvider(ISingletonMemoryProvider innerService) public Task> GetRunnableInstances(DateTime asAt) => _innerService.GetRunnableInstances(asAt); - public Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) => _innerService.GetSubcriptions(eventName, eventKey, asOf); + public Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) => _innerService.GetSubscriptions(eventName, eventKey, asOf); public Task GetWorkflowInstance(string Id) => _innerService.GetWorkflowInstance(Id); @@ -49,5 +49,12 @@ public TransientMemoryPersistenceProvider(ISingletonMemoryProvider innerService) public Task PersistWorkflow(WorkflowInstance workflow) => _innerService.PersistWorkflow(workflow); public Task TerminateSubscription(string eventSubscriptionId) => _innerService.TerminateSubscription(eventSubscriptionId); + public Task GetSubscription(string eventSubscriptionId) => _innerService.GetSubscription(eventSubscriptionId); + + public Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) => _innerService.GetFirstOpenSubscription(eventName, eventKey, asOf); + + public Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) => _innerService.SetSubscriptionToken(eventSubscriptionId, token, workerId, expiry); + + public Task ClearSubscriptionToken(string eventSubscriptionId, string token) => _innerService.ClearSubscriptionToken(eventSubscriptionId, token); } } diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index 65ad3fda9..5b45b2a3b 100755 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -48,9 +48,11 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition { WorkflowId = workflow.Id, StepId = pointer.StepId, + ExecutionPointerId = pointer.Id, EventName = pointer.EventName, EventKey = pointer.EventKey, - SubscribeAsOf = result.EventAsOf + SubscribeAsOf = result.EventAsOf, + SubscriptionData = result.SubscriptionData }); } diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index 1cf005620..e405c02b3 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -437,5 +437,24 @@ public IStepBuilder CancelCondition(Expression Activity(string activityName, Expression> parameters = null, Expression> effectiveDate = null, Expression> cancelCondition = null) + { + var newStep = new WorkflowStep(); + newStep.CancelCondition = cancelCondition; + + WorkflowBuilder.AddStep(newStep); + var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); + stepBuilder.Input((step) => step.ActivityName, (data) => activityName); + + if (parameters != null) + stepBuilder.Input((step) => step.Parameters, parameters); + + if (effectiveDate != null) + stepBuilder.Input((step) => step.EffectiveDate, effectiveDate); + + Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + return stepBuilder; + } } } diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index e1d72c1ed..9092e6487 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -19,6 +19,7 @@ public class WorkflowHost : IWorkflowHost, IDisposable private readonly IEnumerable _backgroundTasks; private readonly IWorkflowController _workflowController; + private readonly IActivityController _activityController; public event StepErrorEventHandler OnStepError; public event LifeCycleEventHandler OnLifeCycleEvent; @@ -34,7 +35,7 @@ public class WorkflowHost : IWorkflowHost, IDisposable private readonly ILifeCycleEventHub _lifeCycleEventHub; private readonly ISearchIndex _searchIndex; - public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, WorkflowOptions options, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IEnumerable backgroundTasks, IWorkflowController workflowController, ILifeCycleEventHub lifeCycleEventHub, ISearchIndex searchIndex) + public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, WorkflowOptions options, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IEnumerable backgroundTasks, IWorkflowController workflowController, ILifeCycleEventHub lifeCycleEventHub, ISearchIndex searchIndex, IActivityController activityController) { PersistenceStore = persistenceStore; QueueProvider = queueProvider; @@ -46,6 +47,7 @@ public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueP _backgroundTasks = backgroundTasks; _workflowController = workflowController; _searchIndex = searchIndex; + _activityController = activityController; _lifeCycleEventHub = lifeCycleEventHub; _lifeCycleEventHub.Subscribe(HandleLifeCycleEvent); } @@ -163,5 +165,25 @@ public void Dispose() if (!_shutdown) Stop(); } + + public Task GetPendingActivity(string activityName, string workerId, TimeSpan? timeout = null) + { + return _activityController.GetPendingActivity(activityName, workerId, timeout); + } + + public Task ReleaseActivityToken(string token) + { + return _activityController.ReleaseActivityToken(token); + } + + public Task SubmitActivitySuccess(string token, object result) + { + return _activityController.SubmitActivitySuccess(token, result); + } + + public Task SubmitActivityFailure(string token, object result) + { + return _activityController.SubmitActivityFailure(token, result); + } } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 96b49545a..8ee703846 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -16,10 +16,11 @@ false Workflow Core is a light weight workflow engine targeting .NET Standard. 2.2.0 - 2.2.0.0 - 2.2.0.0 + 3.0.0.0 + 3.0.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png + 3.0.0 diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs index f3c3d996e..cf3f9183c 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs @@ -100,10 +100,14 @@ internal static PersistedSubscription ToPersistable(this EventSubscription insta result.EventKey = instance.EventKey; result.EventName = instance.EventName; result.StepId = instance.StepId; + result.ExecutionPointerId = instance.ExecutionPointerId; result.WorkflowId = instance.WorkflowId; result.SubscribeAsOf = DateTime.SpecifyKind(instance.SubscribeAsOf, DateTimeKind.Utc); result.SubscriptionData = JsonConvert.SerializeObject(instance.SubscriptionData, SerializerSettings); - + result.ExternalToken = instance.ExternalToken; + result.ExternalTokenExpiry = instance.ExternalTokenExpiry; + result.ExternalWorkerId = instance.ExternalWorkerId; + return result; } @@ -193,9 +197,13 @@ internal static EventSubscription ToEventSubscription(this PersistedSubscription result.EventKey = instance.EventKey; result.EventName = instance.EventName; result.StepId = instance.StepId; + result.ExecutionPointerId = instance.ExecutionPointerId; result.WorkflowId = instance.WorkflowId; result.SubscribeAsOf = DateTime.SpecifyKind(instance.SubscribeAsOf, DateTimeKind.Utc); result.SubscriptionData = JsonConvert.DeserializeObject(instance.SubscriptionData, SerializerSettings); + result.ExternalToken = instance.ExternalToken; + result.ExternalTokenExpiry = instance.ExternalTokenExpiry; + result.ExternalWorkerId = instance.ExternalWorkerId; return result; } diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs index c3be55164..f8fe0cca0 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs @@ -20,6 +20,9 @@ public class PersistedSubscription public int StepId { get; set; } + [MaxLength(200)] + public string ExecutionPointerId { get; set; } + [MaxLength(200)] public string EventName { get; set; } @@ -29,5 +32,13 @@ public class PersistedSubscription public DateTime SubscribeAsOf { get; set; } public string SubscriptionData { get; set; } + + [MaxLength(200)] + public string ExternalToken { get; set; } + + [MaxLength(200)] + public string ExternalWorkerId { get; set; } + + public DateTime? ExternalTokenExpiry { get; set; } } } diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs index 4531f92b1..3af7354de 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs @@ -182,7 +182,7 @@ public virtual void EnsureStoreExists() } } - public async Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) { using (var db = ConstructDbContext()) { @@ -309,5 +309,64 @@ private WorkflowDbContext ConstructDbContext() return _contextFactory.Build(); } + public async Task GetSubscription(string eventSubscriptionId) + { + using (var db = ConstructDbContext()) + { + var uid = new Guid(eventSubscriptionId); + var raw = await db.Set().FirstOrDefaultAsync(x => x.SubscriptionId == uid); + + return raw?.ToEventSubscription(); + } + } + + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + { + using (var db = ConstructDbContext()) + { + var raw = await db.Set().FirstOrDefaultAsync(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf && x.ExternalToken == null); + + return raw?.ToEventSubscription(); + } + } + + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + { + using (var db = ConstructDbContext()) + { + var uid = new Guid(eventSubscriptionId); + var existingEntity = await db.Set() + .Where(x => x.SubscriptionId == uid) + .AsTracking() + .FirstAsync(); + + existingEntity.ExternalToken = token; + existingEntity.ExternalWorkerId = workerId; + existingEntity.ExternalTokenExpiry = expiry; + await db.SaveChangesAsync(); + + return true; + } + } + + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + { + using (var db = ConstructDbContext()) + { + var uid = new Guid(eventSubscriptionId); + var existingEntity = await db.Set() + .Where(x => x.SubscriptionId == uid) + .AsTracking() + .FirstAsync(); + + if (existingEntity.ExternalToken != token) + throw new InvalidOperationException(); + + existingEntity.ExternalToken = null; + existingEntity.ExternalWorkerId = null; + existingEntity.ExternalTokenExpiry = null; + await db.SaveChangesAsync(); + } + } } } diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 129b4f335..7cbcd93b4 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -16,8 +16,9 @@ false 2.2.0 Base package for Workflow-core peristence providers using entity framework - 2.2.0.0 - 2.2.0.0 + 3.0.0.0 + 3.0.0.0 + 3.0.0 diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index bb8dc59ed..f58aa84ef 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -57,9 +57,13 @@ static MongoPersistenceProvider() x.MapProperty(y => y.EventName); x.MapProperty(y => y.EventKey); x.MapProperty(y => y.StepId); + x.MapProperty(y => y.ExecutionPointerId); x.MapProperty(y => y.WorkflowId); x.MapProperty(y => y.SubscribeAsOf); x.MapProperty(y => y.SubscriptionData); + x.MapProperty(y => y.ExternalToken); + x.MapProperty(y => y.ExternalTokenExpiry); + x.MapProperty(y => y.ExternalWorkerId); }); BsonClassMap.RegisterClassMap(x => @@ -170,12 +174,47 @@ public async Task TerminateSubscription(string eventSubscriptionId) await EventSubscriptions.DeleteOneAsync(x => x.Id == eventSubscriptionId); } + public async Task GetSubscription(string eventSubscriptionId) + { + var result = await EventSubscriptions.FindAsync(x => x.Id == eventSubscriptionId); + return await result.FirstAsync(); + } + + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + { + var query = EventSubscriptions + .Find(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf && x.ExternalToken == null); + + return await query.FirstOrDefaultAsync(); + } + + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + { + var update = Builders.Update + .Set(x => x.ExternalToken, token) + .Set(x => x.ExternalTokenExpiry, expiry) + .Set(x => x.ExternalWorkerId, workerId); + + var result = await EventSubscriptions.UpdateOneAsync(x => x.Id == eventSubscriptionId && x.ExternalToken == null, update); + return (result.ModifiedCount > 0); + } + + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + { + var update = Builders.Update + .Set(x => x.ExternalToken, null) + .Set(x => x.ExternalTokenExpiry, null) + .Set(x => x.ExternalWorkerId, null); + + await EventSubscriptions.UpdateOneAsync(x => x.Id == eventSubscriptionId && x.ExternalToken == token, update); + } + public void EnsureStoreExists() { } - public async Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) { var query = EventSubscriptions .Find(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf); diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 19fdbad79..00b213d62 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -16,8 +16,9 @@ false 2.2.0 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 2.2.0.0 - 2.2.0.0 + 3.0.0.0 + 3.0.0.0 + 3.0.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.Designer.cs new file mode 100644 index 000000000..ddc889d7a --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.Designer.cs @@ -0,0 +1,324 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using WorkflowCore.Persistence.PostgreSQL; + +namespace WorkflowCore.Persistence.PostgreSQL.Migrations +{ + [DbContext(typeof(PostgresContext))] + [Migration("20191221210630_Activities")] + partial class Activities + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .HasAnnotation("ProductVersion", "3.1.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("EventData") + .HasColumnType("text"); + + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("EventKey") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("EventName") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("EventTime") + .HasColumnType("timestamp without time zone"); + + b.Property("IsProcessed") + .HasColumnType("boolean"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ErrorTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExecutionPointerId") + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("WorkflowId") + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Children") + .HasColumnType("text"); + + b.Property("ContextItem") + .HasColumnType("text"); + + b.Property("EndTime") + .HasColumnType("timestamp without time zone"); + + b.Property("EventData") + .HasColumnType("text"); + + b.Property("EventKey") + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("EventName") + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("EventPublished") + .HasColumnType("boolean"); + + b.Property("Id") + .HasColumnType("character varying(50)") + .HasMaxLength(50); + + b.Property("Outcome") + .HasColumnType("text"); + + b.Property("PersistenceData") + .HasColumnType("text"); + + b.Property("PredecessorId") + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("RetryCount") + .HasColumnType("integer"); + + b.Property("Scope") + .HasColumnType("text"); + + b.Property("SleepUntil") + .HasColumnType("timestamp without time zone"); + + b.Property("StartTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("StepId") + .HasColumnType("integer"); + + b.Property("StepName") + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("WorkflowId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AttributeKey") + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("AttributeValue") + .HasColumnType("text"); + + b.Property("ExecutionPointerId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("EventKey") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("EventName") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("ExecutionPointerId") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("ExternalToken") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("ExternalTokenExpiry") + .HasColumnType("timestamp without time zone"); + + b.Property("ExternalWorkerId") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("StepId") + .HasColumnType("integer"); + + b.Property("SubscribeAsOf") + .HasColumnType("timestamp without time zone"); + + b.Property("SubscriptionData") + .HasColumnType("text"); + + b.Property("SubscriptionId") + .HasColumnType("uuid") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CompleteTime") + .HasColumnType("timestamp without time zone"); + + b.Property("CreateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("character varying(500)") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasColumnType("uuid") + .HasMaxLength(200); + + b.Property("NextExecution") + .HasColumnType("bigint"); + + b.Property("Reference") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Version") + .HasColumnType("integer"); + + b.Property("WorkflowDefinitionId") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.cs new file mode 100644 index 000000000..81787cf5b --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.cs @@ -0,0 +1,170 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace WorkflowCore.Persistence.PostgreSQL.Migrations +{ + public partial class Activities : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "Workflow", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "Subscription", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + migrationBuilder.AddColumn( + name: "ExecutionPointerId", + schema: "wfc", + table: "Subscription", + maxLength: 200, + nullable: true); + + migrationBuilder.AddColumn( + name: "ExternalToken", + schema: "wfc", + table: "Subscription", + maxLength: 200, + nullable: true); + + migrationBuilder.AddColumn( + name: "ExternalTokenExpiry", + schema: "wfc", + table: "Subscription", + nullable: true); + + migrationBuilder.AddColumn( + name: "ExternalWorkerId", + schema: "wfc", + table: "Subscription", + maxLength: 200, + nullable: true); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "ExtensionAttribute", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "ExecutionPointer", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "ExecutionError", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "Event", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ExecutionPointerId", + schema: "wfc", + table: "Subscription"); + + migrationBuilder.DropColumn( + name: "ExternalToken", + schema: "wfc", + table: "Subscription"); + + migrationBuilder.DropColumn( + name: "ExternalTokenExpiry", + schema: "wfc", + table: "Subscription"); + + migrationBuilder.DropColumn( + name: "ExternalWorkerId", + schema: "wfc", + table: "Subscription"); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "Workflow", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "Subscription", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "ExtensionAttribute", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "ExecutionPointer", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "ExecutionError", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + migrationBuilder.AlterColumn( + name: "PersistenceId", + schema: "wfc", + table: "Event", + nullable: false, + oldClrType: typeof(long)) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs index d37d4eb48..71ab82c32 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs @@ -15,28 +15,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) - .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .HasAnnotation("ProductVersion", "3.1.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("EventData"); + b.Property("EventData") + .HasColumnType("text"); - b.Property("EventId"); + b.Property("EventId") + .HasColumnType("uuid"); b.Property("EventKey") + .HasColumnType("character varying(200)") .HasMaxLength(200); b.Property("EventName") + .HasColumnType("character varying(200)") .HasMaxLength(200); - b.Property("EventTime"); + b.Property("EventTime") + .HasColumnType("timestamp without time zone"); - b.Property("IsProcessed"); + b.Property("IsProcessed") + .HasColumnType("boolean"); b.HasKey("PersistenceId"); @@ -55,16 +63,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("ErrorTime"); + b.Property("ErrorTime") + .HasColumnType("timestamp without time zone"); b.Property("ExecutionPointerId") + .HasColumnType("character varying(100)") .HasMaxLength(100); - b.Property("Message"); + b.Property("Message") + .HasColumnType("text"); b.Property("WorkflowId") + .HasColumnType("character varying(100)") .HasMaxLength(100); b.HasKey("PersistenceId"); @@ -75,52 +89,74 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("Active"); + b.Property("Active") + .HasColumnType("boolean"); - b.Property("Children"); + b.Property("Children") + .HasColumnType("text"); - b.Property("ContextItem"); + b.Property("ContextItem") + .HasColumnType("text"); - b.Property("EndTime"); + b.Property("EndTime") + .HasColumnType("timestamp without time zone"); - b.Property("EventData"); + b.Property("EventData") + .HasColumnType("text"); b.Property("EventKey") + .HasColumnType("character varying(100)") .HasMaxLength(100); b.Property("EventName") + .HasColumnType("character varying(100)") .HasMaxLength(100); - b.Property("EventPublished"); + b.Property("EventPublished") + .HasColumnType("boolean"); b.Property("Id") + .HasColumnType("character varying(50)") .HasMaxLength(50); - b.Property("Outcome"); + b.Property("Outcome") + .HasColumnType("text"); - b.Property("PersistenceData"); + b.Property("PersistenceData") + .HasColumnType("text"); b.Property("PredecessorId") + .HasColumnType("character varying(100)") .HasMaxLength(100); - b.Property("RetryCount"); + b.Property("RetryCount") + .HasColumnType("integer"); - b.Property("Scope"); + b.Property("Scope") + .HasColumnType("text"); - b.Property("SleepUntil"); + b.Property("SleepUntil") + .HasColumnType("timestamp without time zone"); - b.Property("StartTime"); + b.Property("StartTime") + .HasColumnType("timestamp without time zone"); - b.Property("Status"); + b.Property("Status") + .HasColumnType("integer"); - b.Property("StepId"); + b.Property("StepId") + .HasColumnType("integer"); b.Property("StepName") + .HasColumnType("character varying(100)") .HasMaxLength(100); - b.Property("WorkflowId"); + b.Property("WorkflowId") + .HasColumnType("bigint"); b.HasKey("PersistenceId"); @@ -132,14 +168,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("AttributeKey") + .HasColumnType("character varying(100)") .HasMaxLength(100); - b.Property("AttributeValue"); + b.Property("AttributeValue") + .HasColumnType("text"); - b.Property("ExecutionPointerId"); + b.Property("ExecutionPointerId") + .HasColumnType("bigint"); b.HasKey("PersistenceId"); @@ -151,24 +192,48 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("EventKey") + .HasColumnType("character varying(200)") .HasMaxLength(200); b.Property("EventName") + .HasColumnType("character varying(200)") .HasMaxLength(200); - b.Property("StepId"); + b.Property("ExecutionPointerId") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("ExternalToken") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("ExternalTokenExpiry") + .HasColumnType("timestamp without time zone"); + + b.Property("ExternalWorkerId") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("StepId") + .HasColumnType("integer"); - b.Property("SubscribeAsOf"); + b.Property("SubscribeAsOf") + .HasColumnType("timestamp without time zone"); - b.Property("SubscriptionData"); + b.Property("SubscriptionData") + .HasColumnType("text"); b.Property("SubscriptionId") + .HasColumnType("uuid") .HasMaxLength(200); b.Property("WorkflowId") + .HasColumnType("character varying(200)") .HasMaxLength(200); b.HasKey("PersistenceId"); @@ -186,30 +251,42 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("CompleteTime"); + b.Property("CompleteTime") + .HasColumnType("timestamp without time zone"); - b.Property("CreateTime"); + b.Property("CreateTime") + .HasColumnType("timestamp without time zone"); - b.Property("Data"); + b.Property("Data") + .HasColumnType("text"); b.Property("Description") + .HasColumnType("character varying(500)") .HasMaxLength(500); b.Property("InstanceId") + .HasColumnType("uuid") .HasMaxLength(200); - b.Property("NextExecution"); + b.Property("NextExecution") + .HasColumnType("bigint"); b.Property("Reference") + .HasColumnType("character varying(200)") .HasMaxLength(200); - b.Property("Status"); + b.Property("Status") + .HasColumnType("integer"); - b.Property("Version"); + b.Property("Version") + .HasColumnType("integer"); b.Property("WorkflowDefinitionId") + .HasColumnType("character varying(200)") .HasMaxLength(200); b.HasKey("PersistenceId"); @@ -227,7 +304,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") .WithMany("ExecutionPointers") .HasForeignKey("WorkflowId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => @@ -235,7 +313,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") .WithMany("ExtensionAttributes") .HasForeignKey("ExecutionPointerId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); }); #pragma warning restore 612, 618 } diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index f3436fdf7..d315dea06 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -16,8 +16,9 @@ false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. 2.2.0 - 2.2.0.0 - 2.2.0.0 + 3.0.0.0 + 3.0.0.0 + 3.0.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191221210710_Activities.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191221210710_Activities.Designer.cs new file mode 100644 index 000000000..b7e6afd54 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191221210710_Activities.Designer.cs @@ -0,0 +1,336 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WorkflowCore.Persistence.SqlServer; + +namespace WorkflowCore.Persistence.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerContext))] + [Migration("20191221210710_Activities")] + partial class Activities + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("EventData") + .HasColumnType("nvarchar(max)"); + + b.Property("EventId") + .HasColumnType("uniqueidentifier"); + + b.Property("EventKey") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("EventName") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("EventTime") + .HasColumnType("datetime2"); + + b.Property("IsProcessed") + .HasColumnType("bit"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ErrorTime") + .HasColumnType("datetime2"); + + b.Property("ExecutionPointerId") + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("Message") + .HasColumnType("nvarchar(max)"); + + b.Property("WorkflowId") + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Children") + .HasColumnType("nvarchar(max)"); + + b.Property("ContextItem") + .HasColumnType("nvarchar(max)"); + + b.Property("EndTime") + .HasColumnType("datetime2"); + + b.Property("EventData") + .HasColumnType("nvarchar(max)"); + + b.Property("EventKey") + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("EventName") + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("EventPublished") + .HasColumnType("bit"); + + b.Property("Id") + .HasColumnType("nvarchar(50)") + .HasMaxLength(50); + + b.Property("Outcome") + .HasColumnType("nvarchar(max)"); + + b.Property("PersistenceData") + .HasColumnType("nvarchar(max)"); + + b.Property("PredecessorId") + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Scope") + .HasColumnType("nvarchar(max)"); + + b.Property("SleepUntil") + .HasColumnType("datetime2"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("StepName") + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("WorkflowId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AttributeKey") + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("AttributeValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutionPointerId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("EventKey") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("EventName") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("ExecutionPointerId") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("ExternalToken") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("ExternalTokenExpiry") + .HasColumnType("datetime2"); + + b.Property("ExternalWorkerId") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("SubscribeAsOf") + .HasColumnType("datetime2"); + + b.Property("SubscriptionData") + .HasColumnType("nvarchar(max)"); + + b.Property("SubscriptionId") + .HasColumnType("uniqueidentifier") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CompleteTime") + .HasColumnType("datetime2"); + + b.Property("CreateTime") + .HasColumnType("datetime2"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(500)") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasColumnType("uniqueidentifier") + .HasMaxLength(200); + + b.Property("NextExecution") + .HasColumnType("bigint"); + + b.Property("Reference") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Version") + .HasColumnType("int"); + + b.Property("WorkflowDefinitionId") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow","wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191221210710_Activities.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191221210710_Activities.cs new file mode 100644 index 000000000..70dfb4ac3 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20191221210710_Activities.cs @@ -0,0 +1,61 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.SqlServer.Migrations +{ + public partial class Activities : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ExecutionPointerId", + schema: "wfc", + table: "Subscription", + maxLength: 200, + nullable: true); + + migrationBuilder.AddColumn( + name: "ExternalToken", + schema: "wfc", + table: "Subscription", + maxLength: 200, + nullable: true); + + migrationBuilder.AddColumn( + name: "ExternalTokenExpiry", + schema: "wfc", + table: "Subscription", + nullable: true); + + migrationBuilder.AddColumn( + name: "ExternalWorkerId", + schema: "wfc", + table: "Subscription", + maxLength: 200, + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ExecutionPointerId", + schema: "wfc", + table: "Subscription"); + + migrationBuilder.DropColumn( + name: "ExternalToken", + schema: "wfc", + table: "Subscription"); + + migrationBuilder.DropColumn( + name: "ExternalTokenExpiry", + schema: "wfc", + table: "Subscription"); + + migrationBuilder.DropColumn( + name: "ExternalWorkerId", + schema: "wfc", + table: "Subscription"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs index 12b349dcc..8ecddd28e 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs @@ -15,7 +15,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("ProductVersion", "3.1.0") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -23,21 +23,30 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Property("PersistenceId") .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("EventData"); + b.Property("EventData") + .HasColumnType("nvarchar(max)"); - b.Property("EventId"); + b.Property("EventId") + .HasColumnType("uniqueidentifier"); b.Property("EventKey") + .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("EventName") + .HasColumnType("nvarchar(200)") .HasMaxLength(200); - b.Property("EventTime"); + b.Property("EventTime") + .HasColumnType("datetime2"); - b.Property("IsProcessed"); + b.Property("IsProcessed") + .HasColumnType("bit"); b.HasKey("PersistenceId"); @@ -57,16 +66,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Property("PersistenceId") .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("ErrorTime"); + b.Property("ErrorTime") + .HasColumnType("datetime2"); b.Property("ExecutionPointerId") + .HasColumnType("nvarchar(100)") .HasMaxLength(100); - b.Property("Message"); + b.Property("Message") + .HasColumnType("nvarchar(max)"); b.Property("WorkflowId") + .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.HasKey("PersistenceId"); @@ -78,52 +94,75 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Property("PersistenceId") .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("Active"); + b.Property("Active") + .HasColumnType("bit"); - b.Property("Children"); + b.Property("Children") + .HasColumnType("nvarchar(max)"); - b.Property("ContextItem"); + b.Property("ContextItem") + .HasColumnType("nvarchar(max)"); - b.Property("EndTime"); + b.Property("EndTime") + .HasColumnType("datetime2"); - b.Property("EventData"); + b.Property("EventData") + .HasColumnType("nvarchar(max)"); b.Property("EventKey") + .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("EventName") + .HasColumnType("nvarchar(100)") .HasMaxLength(100); - b.Property("EventPublished"); + b.Property("EventPublished") + .HasColumnType("bit"); b.Property("Id") + .HasColumnType("nvarchar(50)") .HasMaxLength(50); - b.Property("Outcome"); + b.Property("Outcome") + .HasColumnType("nvarchar(max)"); - b.Property("PersistenceData"); + b.Property("PersistenceData") + .HasColumnType("nvarchar(max)"); b.Property("PredecessorId") + .HasColumnType("nvarchar(100)") .HasMaxLength(100); - b.Property("RetryCount"); + b.Property("RetryCount") + .HasColumnType("int"); - b.Property("Scope"); + b.Property("Scope") + .HasColumnType("nvarchar(max)"); - b.Property("SleepUntil"); + b.Property("SleepUntil") + .HasColumnType("datetime2"); - b.Property("StartTime"); + b.Property("StartTime") + .HasColumnType("datetime2"); - b.Property("Status"); + b.Property("Status") + .HasColumnType("int"); - b.Property("StepId"); + b.Property("StepId") + .HasColumnType("int"); b.Property("StepName") + .HasColumnType("nvarchar(100)") .HasMaxLength(100); - b.Property("WorkflowId"); + b.Property("WorkflowId") + .HasColumnType("bigint"); b.HasKey("PersistenceId"); @@ -136,14 +175,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Property("PersistenceId") .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("AttributeKey") + .HasColumnType("nvarchar(100)") .HasMaxLength(100); - b.Property("AttributeValue"); + b.Property("AttributeValue") + .HasColumnType("nvarchar(max)"); - b.Property("ExecutionPointerId"); + b.Property("ExecutionPointerId") + .HasColumnType("bigint"); b.HasKey("PersistenceId"); @@ -156,24 +201,49 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Property("PersistenceId") .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("EventKey") + .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("EventName") + .HasColumnType("nvarchar(200)") .HasMaxLength(200); - b.Property("StepId"); + b.Property("ExecutionPointerId") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("ExternalToken") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("ExternalTokenExpiry") + .HasColumnType("datetime2"); + + b.Property("ExternalWorkerId") + .HasColumnType("nvarchar(200)") + .HasMaxLength(200); + + b.Property("StepId") + .HasColumnType("int"); - b.Property("SubscribeAsOf"); + b.Property("SubscribeAsOf") + .HasColumnType("datetime2"); - b.Property("SubscriptionData"); + b.Property("SubscriptionData") + .HasColumnType("nvarchar(max)"); b.Property("SubscriptionId") + .HasColumnType("uniqueidentifier") .HasMaxLength(200); b.Property("WorkflowId") + .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("PersistenceId"); @@ -192,30 +262,43 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Property("PersistenceId") .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("CompleteTime"); + b.Property("CompleteTime") + .HasColumnType("datetime2"); - b.Property("CreateTime"); + b.Property("CreateTime") + .HasColumnType("datetime2"); - b.Property("Data"); + b.Property("Data") + .HasColumnType("nvarchar(max)"); b.Property("Description") + .HasColumnType("nvarchar(500)") .HasMaxLength(500); b.Property("InstanceId") + .HasColumnType("uniqueidentifier") .HasMaxLength(200); - b.Property("NextExecution"); + b.Property("NextExecution") + .HasColumnType("bigint"); b.Property("Reference") + .HasColumnType("nvarchar(200)") .HasMaxLength(200); - b.Property("Status"); + b.Property("Status") + .HasColumnType("int"); - b.Property("Version"); + b.Property("Version") + .HasColumnType("int"); b.Property("WorkflowDefinitionId") + .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("PersistenceId"); @@ -233,7 +316,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") .WithMany("ExecutionPointers") .HasForeignKey("WorkflowId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => @@ -241,7 +325,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") .WithMany("ExtensionAttributes") .HasForeignKey("ExecutionPointerId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); }); #pragma warning restore 612, 618 } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 198b6b9a9..d5a181360 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -17,8 +17,9 @@ false 2.2.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 2.2.0.0 - 2.2.0.0 + 3.0.0.0 + 3.0.0.0 + 3.0.0 diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 35022ed26..ef7de2537 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,9 +16,10 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 2.2.0 - 2.2.0.0 - 2.2.0.0 + 3.0.0 + 3.0.0.0 + 3.0.0.0 + 3.0.0 diff --git a/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs index 49183847f..6ec9b0f8b 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs @@ -82,31 +82,54 @@ public static WorkflowInstance ToWorkflowInstance(this Dictionary ToDynamoMap(this EventSubscription source) { - return new Dictionary + var result = new Dictionary { ["id"] = new AttributeValue(source.Id), ["event_name"] = new AttributeValue(source.EventName), ["event_key"] = new AttributeValue(source.EventKey), ["workflow_id"] = new AttributeValue(source.WorkflowId), + ["execution_pointer_id"] = new AttributeValue(source.ExecutionPointerId), ["step_id"] = new AttributeValue(source.StepId.ToString()), ["subscribe_as_of"] = new AttributeValue() { N = source.SubscribeAsOf.Ticks.ToString() }, ["subscription_data"] = new AttributeValue(JsonConvert.SerializeObject(source.SubscriptionData, SerializerSettings)), ["event_slug"] = new AttributeValue($"{source.EventName}:{source.EventKey}") }; + if (!string.IsNullOrEmpty(source.ExternalToken)) + result["external_token"] = new AttributeValue(source.ExternalToken); + + if (!string.IsNullOrEmpty(source.ExternalWorkerId)) + result["external_worker_id"] = new AttributeValue(source.ExternalWorkerId); + + if (source.ExternalTokenExpiry.HasValue) + result["external_token_expiry"] = new AttributeValue() { N = source.ExternalTokenExpiry.Value.Ticks.ToString()}; + + return result; } public static EventSubscription ToEventSubscription(this Dictionary source) { - return new EventSubscription() + var result = new EventSubscription() { Id = source["id"].S, EventName = source["event_name"].S, EventKey = source["event_key"].S, WorkflowId = source["workflow_id"].S, + ExecutionPointerId = source["execution_pointer_id"].S, StepId = Convert.ToInt32(source["step_id"].S), SubscribeAsOf = new DateTime(Convert.ToInt64(source["subscribe_as_of"].N)), - SubscriptionData = JsonConvert.DeserializeObject(source["subscription_data"].S, SerializerSettings) + SubscriptionData = JsonConvert.DeserializeObject(source["subscription_data"].S, SerializerSettings), }; + + if (source.ContainsKey("external_token")) + result.ExternalToken = source["external_token"].S; + + if (source.ContainsKey("external_worker_id")) + result.ExternalWorkerId = source["external_worker_id"].S; + + if (source.ContainsKey("external_token_expiry")) + result.ExternalTokenExpiry = new DateTime(Int64.Parse(source["external_token_expiry"].N)); + + return result; } public static Dictionary ToDynamoMap(this Event source) diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs index 6930e6322..a78b006c0 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs @@ -121,7 +121,7 @@ private async Task CreateSubscriptionTable() WriteCapacityUnits = 1 } }; - + var createRequest = new CreateTableRequest($"{_tablePrefix}-{DynamoPersistenceProvider.SUBCRIPTION_TABLE}", new List() { new KeySchemaElement("id", KeyType.HASH) diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index 9669d8346..afc531b7a 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -177,7 +177,7 @@ public async Task CreateEventSubscription(EventSubscription subscription return subscription.Id; } - public async Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) { var result = new List(); var asOfTicks = asOf.ToUniversalTime().Ticks; @@ -369,5 +369,105 @@ public void EnsureStoreExists() { _provisioner.ProvisionTables().Wait(); } + + public async Task GetSubscription(string eventSubscriptionId) + { + var req = new GetItemRequest() + { + TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", + Key = new Dictionary + { + { "id", new AttributeValue(eventSubscriptionId) } + } + }; + var response = await _client.GetItemAsync(req); + + return response.Item.ToEventSubscription(); + } + + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + { + var result = new List(); + var asOfTicks = asOf.ToUniversalTime().Ticks; + + var request = new QueryRequest() + { + TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", + IndexName = "ix_slug", + Select = "ALL_PROJECTED_ATTRIBUTES", + KeyConditionExpression = "event_slug = :slug and subscribe_as_of <= :as_of", + FilterExpression = "attribute_not_exists(external_token)", + Limit = 1, + ExpressionAttributeValues = new Dictionary + { + { + ":slug", new AttributeValue($"{eventName}:{eventKey}") + }, + { + ":as_of", new AttributeValue() + { + N = Convert.ToString(asOfTicks) + } + } + }, + ScanIndexForward = true + }; + + var response = await _client.QueryAsync(request); + + foreach (var item in response.Items) + result.Add(item.ToEventSubscription()); + + return result.FirstOrDefault(); + } + + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + { + var request = new UpdateItemRequest() + { + TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", + Key = new Dictionary + { + { "id", new AttributeValue(eventSubscriptionId) } + }, + UpdateExpression = "SET external_token = :external_token, external_worker_id = :external_worker_id, external_token_expiry = :external_token_expiry", + ConditionExpression = "attribute_not_exists(external_token)", + ExpressionAttributeValues = new Dictionary() + { + { ":external_token" , new AttributeValue() { S = token } }, + { ":external_worker_id" , new AttributeValue() { S = workerId } }, + { ":external_token_expiry" , new AttributeValue() { N = expiry.Ticks.ToString() } } + } + }; + try + { + await _client.UpdateItemAsync(request); + return true; + } + catch (ConditionalCheckFailedException) + { + return false; + } + } + + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + { + var request = new UpdateItemRequest() + { + TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", + Key = new Dictionary + { + { "id", new AttributeValue(eventSubscriptionId) } + }, + UpdateExpression = "REMOVE external_token, external_worker_id, external_token_expiry", + ConditionExpression = "external_token = :external_token", + ExpressionAttributeValues = new Dictionary() + { + { ":external_token" , new AttributeValue() { S = token } }, + } + }; + + await _client.UpdateItemAsync(request); + } } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 4933cc231..a20f3c440 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -12,7 +12,8 @@ https://github.com/danielgerlag/workflow-core.git git 2.2.0 - 2.2.0.0 + 3.0.0.0 + 3.0.0 diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index 373ac14a6..621f01e8b 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -97,7 +97,7 @@ public async Task CreateEventSubscription(EventSubscription subscription return subscription.Id; } - public async Task> GetSubcriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) { var result = new List(); var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{SUBSCRIPTION_SET}.{EVENTSLUG_INDEX}.{eventName}-{eventKey}", -1, asOf.Ticks); @@ -120,6 +120,42 @@ public async Task TerminateSubscription(string eventSubscriptionId) await _redis.SortedSetRemoveAsync($"{_prefix}.{SUBSCRIPTION_SET}.{EVENTSLUG_INDEX}.{existing.EventName}-{existing.EventKey}", eventSubscriptionId); } + public async Task GetSubscription(string eventSubscriptionId) + { + var raw = await _redis.HashGetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId); + return JsonConvert.DeserializeObject(raw, _serializerSettings); + } + + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + { + return (await GetSubscriptions(eventName, eventKey, asOf)).FirstOrDefault(sub => string.IsNullOrEmpty(sub.ExternalToken)); + } + + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + { + var item = JsonConvert.DeserializeObject(await _redis.HashGetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId), _serializerSettings); + if (item.ExternalToken != null) + return false; + item.ExternalToken = token; + item.ExternalWorkerId = workerId; + item.ExternalTokenExpiry = expiry; + var str = JsonConvert.SerializeObject(item, _serializerSettings); + await _redis.HashSetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId, str); + return true; + } + + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + { + var item = JsonConvert.DeserializeObject(await _redis.HashGetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId), _serializerSettings); + if (item.ExternalToken != token) + return; + item.ExternalToken = null; + item.ExternalWorkerId = null; + item.ExternalTokenExpiry = null; + var str = JsonConvert.SerializeObject(item, _serializerSettings); + await _redis.HashSetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId, str); + } + public async Task CreateEvent(Event newEvent) { newEvent.Id = Guid.NewGuid().ToString(); diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index bf01cee71..1ef23b6e6 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -9,6 +9,7 @@ https://github.com/danielgerlag/workflow-core Redis providers for Workflow Core (Persistence, queueing, distributed locking and event hubs) + 3.0.0 diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index e0a892ecc..4ecd7cd2a 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -24,6 +24,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/samples/WorkflowCore.Sample18/ActivityWorkflow.cs b/src/samples/WorkflowCore.Sample18/ActivityWorkflow.cs new file mode 100644 index 000000000..126e2be15 --- /dev/null +++ b/src/samples/WorkflowCore.Sample18/ActivityWorkflow.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using WorkflowCore.Interface; +using WorkflowCore.Sample18.Steps; + +namespace WorkflowCore.Sample18 +{ + class ActivityWorkflow : IWorkflow + { + public string Id => "activity-sample"; + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Activity("get-approval", (data) => data.Request) + .Output(data => data.ApprovedBy, step => step.Result) + .Then() + .Input(step => step.Message, data => "Approved by " + data.ApprovedBy) + .Then(); + } + } + + class MyData + { + public string Request { get; set; } + public string ApprovedBy { get; set; } + } +} diff --git a/src/samples/WorkflowCore.Sample18/Program.cs b/src/samples/WorkflowCore.Sample18/Program.cs new file mode 100644 index 000000000..68e58af25 --- /dev/null +++ b/src/samples/WorkflowCore.Sample18/Program.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; +using Microsoft.Extensions.Logging.Debug; +using System; +using WorkflowCore.Interface; + +namespace WorkflowCore.Sample18 +{ + class Program + { + static void Main(string[] args) + { + var serviceProvider = ConfigureServices(); + + //start the workflow host + var host = serviceProvider.GetService(); + host.RegisterWorkflow(); + host.Start(); + + Console.WriteLine("Starting workflow..."); + + var workflowId = host.StartWorkflow("activity-sample", new MyData() { Request = "Spend $1,000,000" }).Result; + + var approval = host.GetPendingActivity("get-approval", "worker1", TimeSpan.FromMinutes(1)).Result; + + if (approval != null) + { + Console.WriteLine("Approval required for " + approval.Parameters); + host.SubmitActivitySuccess(approval.Token, "John Smith"); + } + + Console.ReadLine(); + host.Stop(); + } + + private static IServiceProvider ConfigureServices() + { + //setup dependency injection + IServiceCollection services = new ServiceCollection(); + services.AddWorkflow(); + //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); + //services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); + //services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;", true, true)); + services.AddLogging(cfg => + { + cfg.AddConsole(); + cfg.AddDebug(); + }); + + var serviceProvider = services.BuildServiceProvider(); + return serviceProvider; + } + } +} diff --git a/src/samples/WorkflowCore.Sample18/README.md b/src/samples/WorkflowCore.Sample18/README.md new file mode 100644 index 000000000..85823eaf7 --- /dev/null +++ b/src/samples/WorkflowCore.Sample18/README.md @@ -0,0 +1,28 @@ +# Activity sample + +Illustrates how to have your workflow wait for an external activity that is fulfilled by a worker that you implement. + +This workflow will wait for the `get-approval` activity and pass the request string to it as an input. + +```c# +builder + .StartWith() + .Activity("get-approval", (data) => data.Request) + .Output(data => data.ApprovedBy, step => step.Result) + .Then() + .Input(step => step.Message, data => "Approved by " + data.ApprovedBy) + .Then(); +``` + +Then we implement an activity worker to pull pending activities of type `get-approval`, where we can inspect the input and submit a response back to the waiting workflow. + +```c# +var approval = host.GetPendingActivity("get-approval", "worker1", TimeSpan.FromMinutes(1)).Result; + +if (approval != null) +{ + Console.WriteLine("Approval required for " + approval.Parameters); + host.SubmitActivitySuccess(approval.Token, "John Smith"); +} +``` + diff --git a/src/samples/WorkflowCore.Sample18/Steps/CustomMessage.cs b/src/samples/WorkflowCore.Sample18/Steps/CustomMessage.cs new file mode 100644 index 000000000..0d9803de3 --- /dev/null +++ b/src/samples/WorkflowCore.Sample18/Steps/CustomMessage.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample18.Steps +{ + public class CustomMessage : StepBody + { + + public string Message { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine(Message); + return ExecutionResult.Next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample18/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.Sample18/Steps/GoodbyeWorld.cs new file mode 100644 index 000000000..dffce0d6b --- /dev/null +++ b/src/samples/WorkflowCore.Sample18/Steps/GoodbyeWorld.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample18.Steps +{ + public class GoodbyeWorld : StepBody + { + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Goodbye world"); + return ExecutionResult.Next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample18/Steps/HelloWorld.cs b/src/samples/WorkflowCore.Sample18/Steps/HelloWorld.cs new file mode 100644 index 000000000..8440b8cbe --- /dev/null +++ b/src/samples/WorkflowCore.Sample18/Steps/HelloWorld.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample18.Steps +{ + public class HelloWorld : StepBody + { + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Hello world"); + return ExecutionResult.Next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj new file mode 100644 index 000000000..660b47511 --- /dev/null +++ b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj @@ -0,0 +1,20 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + + + + + + + + diff --git a/test/ScratchPad/ElasticTest.cs b/test/ScratchPad/ElasticTest.cs new file mode 100644 index 000000000..0e57c4012 --- /dev/null +++ b/test/ScratchPad/ElasticTest.cs @@ -0,0 +1,81 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using System.Text; +using Amazon; +using Amazon.DynamoDBv2; +using Amazon.Runtime; +using Nest; +using WorkflowCore.Models.Search; + +namespace ScratchPad +{ + public class ElasticTest + { + public static void test(string[] args) + { + IServiceProvider serviceProvider = ConfigureServices(); + + //start the workflow host + var host = serviceProvider.GetService(); + var searchIndex = serviceProvider.GetService(); + + host.RegisterWorkflow(); + host.RegisterWorkflow(); + + host.Start(); + var data1 = new WorkflowCore.Sample03.MyDataClass() { Value1 = 2, Value2 = 3 }; + host.StartWorkflow("PassingDataWorkflow", data1, "quick dog").Wait(); + + var data2 = new WorkflowCore.Sample04.MyDataClass() { Value1 = "test" }; + host.StartWorkflow("EventSampleWorkflow", data2, "alt1 boom").Wait(); + + + var searchResult1 = searchIndex.Search("dog", 0, 10).Result; + var searchResult2 = searchIndex.Search("quick dog", 0, 10).Result; + var searchResult3 = searchIndex.Search("fast", 0, 10).Result; + var searchResult4 = searchIndex.Search("alt1", 0, 10).Result; + var searchResult5 = searchIndex.Search("dogs", 0, 10).Result; + var searchResult6 = searchIndex.Search("test", 0, 10).Result; + var searchResult7 = searchIndex.Search("", 0, 10).Result; + var searchResult8 = searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Reference, "quick dog")).Result; + var searchResult9 = searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Value1, 2)).Result; + + Console.ReadLine(); + host.Stop(); + } + + private static IServiceProvider ConfigureServices() + { + //setup dependency injection + IServiceCollection services = new ServiceCollection(); + services.AddLogging(); + //services.AddWorkflow(); + //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); + services.AddWorkflow(cfg => + { + //var ddbConfig = new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }; + //cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), ddbConfig, "elastic"); + cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://localhost:9200")), "workflows"); + //cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); + //cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); + }); + + services.AddTransient(); + + var serviceProvider = services.BuildServiceProvider(); + + + return serviceProvider; + } + + } + +} + diff --git a/test/ScratchPad/HelloWorld.json b/test/ScratchPad/HelloWorld.json index 596db504e..b947071fa 100644 --- a/test/ScratchPad/HelloWorld.json +++ b/test/ScratchPad/HelloWorld.json @@ -1,30 +1,29 @@ { - "Id": "HelloWorld", + "Id": "Test02", "Version": 1, "Description": "", - "DataType": "ScratchPad.MyDataClass, ScratchPad", + "DataType": "ScratchPad.WfData, ScratchPad", "Steps": [ { "Id": "Hello", "StepType": "ScratchPad.HelloWorld, ScratchPad", - "NextStepId": "Generate" + "NextStepId": "act" }, { - "Id": "Generate", - "StepType": "ScratchPad.GenerateMessage, ScratchPad", + "Id": "act", + "StepType": "WorkflowCore.Primitives.Activity, WorkflowCore", "NextStepId": "Print", - "Outputs": { "Value3": "step.Message" } + "Inputs": + { + "ActivityName": "\"act1\"", + "Parameters": "data.Value1" + }, + "Outputs": { "Value3": "step.Result" } }, { "Id": "Print", - "StepType": "ScratchPad.PrintMessage, ScratchPad", - "NextStepId": "Bye", - "Inputs": { "Message": "data.Value3 + \" - \" + DateTime.Now.ToString()" } - }, - - { - "Id": "Bye", - "StepType": "ScratchPad.GoodbyeWorld, ScratchPad" - } + "StepType": "ScratchPad.CustomMessage, ScratchPad", + "Inputs": { "Message": "data.Value3" } + } ] } \ No newline at end of file diff --git a/test/ScratchPad/Program.cs b/test/ScratchPad/Program.cs index 91b4eb21a..ac3d981b7 100644 --- a/test/ScratchPad/Program.cs +++ b/test/ScratchPad/Program.cs @@ -8,11 +8,7 @@ using WorkflowCore.Interface; using WorkflowCore.Models; using System.Text; -using Amazon; -using Amazon.DynamoDBv2; -using Amazon.Runtime; -using Nest; -using WorkflowCore.Models.Search; +using WorkflowCore.Services.DefinitionStorage; namespace ScratchPad { @@ -24,29 +20,30 @@ public static void Main(string[] args) //start the workflow host var host = serviceProvider.GetService(); - var searchIndex = serviceProvider.GetService(); - - host.RegisterWorkflow(); - host.RegisterWorkflow(); - + var loader = serviceProvider.GetService(); + var activityController = serviceProvider.GetService(); + //host.RegisterWorkflow(); + loader.LoadDefinition(Properties.Resources.HelloWorld, Deserializers.Json); + host.Start(); - var data1 = new WorkflowCore.Sample03.MyDataClass() { Value1 = 2, Value2 = 3 }; - host.StartWorkflow("PassingDataWorkflow", data1, "quick dog").Wait(); - - var data2 = new WorkflowCore.Sample04.MyDataClass() { Value1 = "test" }; - host.StartWorkflow("EventSampleWorkflow", data2, "alt1 boom").Wait(); + + host.StartWorkflow("Test02", 1, new WfData() + { + Value1 = "data1", + Value2 = "data2" + }); + var act = activityController.GetPendingActivity("act1", "worker1", TimeSpan.FromMinutes(1)).Result; - var searchResult1 = searchIndex.Search("dog", 0, 10).Result; - var searchResult2 = searchIndex.Search("quick dog", 0, 10).Result; - var searchResult3 = searchIndex.Search("fast", 0, 10).Result; - var searchResult4 = searchIndex.Search("alt1", 0, 10).Result; - var searchResult5 = searchIndex.Search("dogs", 0, 10).Result; - var searchResult6 = searchIndex.Search("test", 0, 10).Result; - var searchResult7 = searchIndex.Search("", 0, 10).Result; - var searchResult8 = searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Reference, "quick dog")).Result; - var searchResult9 = searchIndex.Search("", 0, 10, ScalarFilter.Equals(x => x.Value1, 2)).Result; + if (act != null) + { + Console.WriteLine("get act " + act.Token); + Console.WriteLine(act.Parameters); + activityController.SubmitActivitySuccess(act.Token, "BOO"); + } + + Console.ReadLine(); host.Stop(); } @@ -62,22 +59,64 @@ private static IServiceProvider ConfigureServices() { //var ddbConfig = new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }; //cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), ddbConfig, "elastic"); - cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://localhost:9200")), "workflows"); + //cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://localhost:9200")), "workflows"); //cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); //cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); }); - services.AddTransient(); - + var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddDebug(); return serviceProvider; } } + + public class HelloWorld : StepBody + { + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Hello world"); + return ExecutionResult.Next(); + } + } + + public class CustomMessage : StepBody + { + public string Message { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine(Message); + return ExecutionResult.Next(); + } + } + + public class WfData + { + public string Value1 { get; set; } + public string Value2 { get; set; } + public string Value3 { get; set; } + } + + public class Test01Workflow : IWorkflow + { + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Activity("act1", (data) => data.Value1) + .Output(data => data.Value3, step => step.Result) + .Then() + .Input(step => step.Message, data => data.Value3); + } + + public string Id => "Test01"; + + public int Version => 1; + + } + } diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 2f99c96b2..89556ede3 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp3.0 ScratchPad Exe ScratchPad @@ -23,11 +23,8 @@ - - - - - + + diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs new file mode 100644 index 000000000..03562bfbd --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using System.Linq; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class ActivityScenario : WorkflowTest + { + public class MyDataClass + { + public object ActivityInput { get; set; } + public object ActivityOutput { get; set; } + } + + public class ActivityInput + { + public string Value1 { get; set; } + public int Value2 { get; set; } + } + + public class ActivityOutput + { + public string Value1 { get; set; } + public int Value2 { get; set; } + } + + public class ActivityWorkflow : IWorkflow + { + public string Id => "ActivityWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .Activity("act-1", data => data.ActivityInput) + .Output(data => data.ActivityOutput, step => step.Result); + } + } + + public ActivityScenario() + { + Setup(); + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(new MyDataClass() { ActivityInput = new ActivityInput() { Value1 = "a", Value2 = 1 } }); + var activity = Host.GetPendingActivity("act-1", "worker1", TimeSpan.FromSeconds(30)).Result; + + if (activity != null) + { + var actInput = (ActivityInput)activity.Parameters; + Host.SubmitActivitySuccess(activity.Token, new ActivityOutput() + { + Value1 = actInput.Value1 + "1", + Value2 = actInput.Value2 + 1 + }); + } + + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + GetData(workflowId).ActivityOutput.Should().BeOfType(); + var outData = (GetData(workflowId).ActivityOutput as ActivityOutput); + outData.Value1.Should().Be("a1"); + outData.Value2.Should().Be(2); + } + } +} diff --git a/test/WorkflowCore.Testing/JsonWorkflowTest.cs b/test/WorkflowCore.Testing/JsonWorkflowTest.cs index cd66550a7..16fd7c232 100644 --- a/test/WorkflowCore.Testing/JsonWorkflowTest.cs +++ b/test/WorkflowCore.Testing/JsonWorkflowTest.cs @@ -76,7 +76,7 @@ protected void WaitForWorkflowToComplete(string workflowId, TimeSpan timeOut) protected IEnumerable GetActiveSubscriptons(string eventName, string eventKey) { - return PersistenceProvider.GetSubcriptions(eventName, eventKey, DateTime.MaxValue).Result; + return PersistenceProvider.GetSubscriptions(eventName, eventKey, DateTime.MaxValue).Result; } protected void WaitForEventSubscription(string eventName, string eventKey, TimeSpan timeOut) diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/test/WorkflowCore.Testing/WorkflowTest.cs index 4c9602148..1a0dc8d81 100644 --- a/test/WorkflowCore.Testing/WorkflowTest.cs +++ b/test/WorkflowCore.Testing/WorkflowTest.cs @@ -75,7 +75,7 @@ protected void WaitForWorkflowToComplete(string workflowId, TimeSpan timeOut) protected IEnumerable GetActiveSubscriptons(string eventName, string eventKey) { - return PersistenceProvider.GetSubcriptions(eventName, eventKey, DateTime.MaxValue).Result; + return PersistenceProvider.GetSubscriptions(eventName, eventKey, DateTime.MaxValue).Result; } protected void WaitForEventSubscription(string eventName, string eventKey, TimeSpan timeOut) diff --git a/test/WorkflowCore.Testing/YamlWorkflowTest.cs b/test/WorkflowCore.Testing/YamlWorkflowTest.cs index def1e0353..58cbb3cad 100644 --- a/test/WorkflowCore.Testing/YamlWorkflowTest.cs +++ b/test/WorkflowCore.Testing/YamlWorkflowTest.cs @@ -76,7 +76,7 @@ protected void WaitForWorkflowToComplete(string workflowId, TimeSpan timeOut) protected IEnumerable GetActiveSubscriptons(string eventName, string eventKey) { - return PersistenceProvider.GetSubcriptions(eventName, eventKey, DateTime.MaxValue).Result; + return PersistenceProvider.GetSubscriptions(eventName, eventKey, DateTime.MaxValue).Result; } protected void WaitForEventSubscription(string eventName, string eventKey, TimeSpan timeOut) diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs new file mode 100644 index 000000000..29682a442 --- /dev/null +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson.Serialization; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("Mongo collection")] + public class MongoActivityScenario : ActivityScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + BsonClassMap.RegisterClassMap(x => x.AutoMap()); + BsonClassMap.RegisterClassMap(x => x.AutoMap()); + + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + } + } +} diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresActivityScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresActivityScenario.cs new file mode 100644 index 000000000..7b63b42dd --- /dev/null +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresActivityScenario.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.PostgreSQL.Scenarios +{ + [Collection("Postgres collection")] + public class PostgresActivityScenario : ActivityScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UsePostgreSQL(PostgresDockerSetup.ScenarioConnectionString, true, true)); + } + } +} diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerActivityScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerActivityScenario.cs new file mode 100644 index 000000000..cf09f78e3 --- /dev/null +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerActivityScenario.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.SqlServer.Scenarios +{ + [Collection("SqlServer collection")] + public class SqlServerActivityScenario : ActivityScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseSqlServer(SqlDockerSetup.ScenarioConnectionString, true, true)); + } + } +} From 49600648accd8c6d0d808bf3336f158f9c61c1d2 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 22 Dec 2019 08:50:42 -0800 Subject: [PATCH 177/462] release notes --- ReleaseNotes/3.0.0.md | 61 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 ReleaseNotes/3.0.0.md diff --git a/ReleaseNotes/3.0.0.md b/ReleaseNotes/3.0.0.md new file mode 100644 index 000000000..348d3a4a2 --- /dev/null +++ b/ReleaseNotes/3.0.0.md @@ -0,0 +1,61 @@ +# Workflow Core 3.0.0 + +### Split DSL into own package + +The JSON and YAML definition features into their own package. + +Migration required for existing projects: + +* Install the `WorkflowCore.DSL` package from nuget. +* Call `AddWorkflowDSL()` on your service collection. + +### Activities + +An activity is defined as an item on an external queue of work, that a workflow can wait for. + +In this example the workflow will wait for `activity-1`, before proceeding. It also passes the value of `data.Value1` to the activity, it then maps the result of the activity to `data.Value2`. + +Then we create a worker to process the queue of activity items. It uses the `GetPendingActivity` method to get an activity and the data that a workflow is waiting for. + + + +```C# +public class ActivityWorkflow : IWorkflow +{ + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Activity("activity-1", (data) => data.Value1) + .Output(data => data.Value2, step => step.Result) + .Then() + .Input(step => step.Message, data => data.Value2); + } + +} +... + +var activity = host.GetPendingActivity("activity-1", "worker1", TimeSpan.FromMinutes(1)).Result; + +if (activity != null) +{ + Console.WriteLine(activity.Parameters); + host.SubmitActivitySuccess(activity.Token, "Some response data"); +} + +``` + +The JSON representation of this step would look like this + +```json +{ + "Id": "activity-step", + "StepType": "WorkflowCore.Primitives.Activity, WorkflowCore", + "Inputs": + { + "ActivityName": "\"activity-1\"", + "Parameters": "data.Value1" + }, + "Outputs": { "Value2": "step.Result" } +} +``` \ No newline at end of file From f99b7c5d95a1994b841ee57c8bfa08266059ad8e Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 22 Dec 2019 09:44:57 -0800 Subject: [PATCH 178/462] recreate Postgres migration with older version --- ...es.Designer.cs => 20191222174302_Activities.Designer.cs} | 2 +- ...221210630_Activities.cs => 20191222174302_Activities.cs} | 0 .../WorkflowCore.Persistence.PostgreSQL.csproj | 6 +++--- .../WorkflowCore.Sample04/WorkflowCore.Sample04.csproj | 2 +- .../WorkflowCore.Sample07/WorkflowCore.Sample07.csproj | 2 +- .../WorkflowCore.Sample08/WorkflowCore.Sample08.csproj | 2 +- .../WorkflowCore.Sample09/WorkflowCore.Sample09.csproj | 2 +- .../WorkflowCore.Sample10/WorkflowCore.Sample10.csproj | 2 +- .../WorkflowCore.Sample12/WorkflowCore.Sample12.csproj | 2 +- .../WorkflowCore.Sample17/WorkflowCore.Sample17.csproj | 2 +- .../WorkflowCore.Tests.PostgreSQL.csproj | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) rename src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/{20191221210630_Activities.Designer.cs => 20191222174302_Activities.Designer.cs} (99%) rename src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/{20191221210630_Activities.cs => 20191222174302_Activities.cs} (100%) diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191222174302_Activities.Designer.cs similarity index 99% rename from src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.Designer.cs rename to src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191222174302_Activities.Designer.cs index ddc889d7a..a067ca244 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191222174302_Activities.Designer.cs @@ -10,7 +10,7 @@ namespace WorkflowCore.Persistence.PostgreSQL.Migrations { [DbContext(typeof(PostgresContext))] - [Migration("20191221210630_Activities")] + [Migration("20191222174302_Activities")] partial class Activities { protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191222174302_Activities.cs similarity index 100% rename from src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191221210630_Activities.cs rename to src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20191222174302_Activities.cs diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index d315dea06..48732f95d 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -3,7 +3,7 @@ Workflow Core PostgreSQL Persistence Provider Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.PostgreSQL WorkflowCore.Persistence.PostgreSQL workflow;.NET;Core;state machine;WorkflowCore;PostgreSQL @@ -28,8 +28,8 @@ - - + + All diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index 4ecd7cd2a..48964fd7b 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.0 WorkflowCore.Sample04 Exe WorkflowCore.Sample04 diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index a6929f4e8..76ac7be9e 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.0 true WorkflowCore.Sample07 Exe diff --git a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj index 40c7596e4..3ff429e28 100644 --- a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj +++ b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.0 WorkflowCore.Sample08 Exe WorkflowCore.Sample08 diff --git a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj index 5db7f6ca0..6f75a0d7d 100644 --- a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj +++ b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + netcoreapp3.0 diff --git a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj index 07135f7b7..6f75a0d7d 100644 --- a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj +++ b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.0 diff --git a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj index 2d1fc738b..9879baab2 100644 --- a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj +++ b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + netcoreapp3.0 diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index 36d4950d0..c28f63d24 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.0 diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 42c419a1a..117e4dc56 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.0 WorkflowCore.Tests.PostgreSQL WorkflowCore.Tests.PostgreSQL true From a1b1dd38ef076bd9665ce76247ffac804cb1f1e1 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 28 Dec 2019 17:48:19 -0800 Subject: [PATCH 179/462] exceptions --- .../Exceptions/NotFoundException.cs | 18 ++++++++++++++++++ .../Exceptions/WorkflowLockedException.cs | 12 ++++++++++++ .../Services/ActivityController.cs | 8 ++++++-- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- ...flowCore.Persistence.EntityFramework.csproj | 2 +- .../Services/MongoPersistenceProvider.cs | 2 +- .../WorkflowCore.Persistence.MongoDB.csproj | 6 +++--- .../WorkflowCore.Persistence.PostgreSQL.csproj | 2 +- .../WorkflowCore.Persistence.SqlServer.csproj | 2 +- .../WorkflowCore.Providers.AWS.csproj | 2 +- .../WorkflowCore.Providers.Redis.csproj | 2 +- 11 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 src/WorkflowCore/Exceptions/NotFoundException.cs create mode 100644 src/WorkflowCore/Exceptions/WorkflowLockedException.cs diff --git a/src/WorkflowCore/Exceptions/NotFoundException.cs b/src/WorkflowCore/Exceptions/NotFoundException.cs new file mode 100644 index 000000000..b85c5073c --- /dev/null +++ b/src/WorkflowCore/Exceptions/NotFoundException.cs @@ -0,0 +1,18 @@ +using System; + +namespace WorkflowCore.Exceptions +{ + public class NotFoundException : Exception + { + + public NotFoundException() : base() + { + + } + + public NotFoundException(string message) : base(message) + { + // + } + } +} \ No newline at end of file diff --git a/src/WorkflowCore/Exceptions/WorkflowLockedException.cs b/src/WorkflowCore/Exceptions/WorkflowLockedException.cs new file mode 100644 index 000000000..23cdaf628 --- /dev/null +++ b/src/WorkflowCore/Exceptions/WorkflowLockedException.cs @@ -0,0 +1,12 @@ +using System; + +namespace WorkflowCore.Exceptions +{ + public class WorkflowLockedException : Exception + { + public WorkflowLockedException(): base() + { + // + } + } +} \ No newline at end of file diff --git a/src/WorkflowCore/Services/ActivityController.cs b/src/WorkflowCore/Services/ActivityController.cs index b832ce596..fafa26e98 100644 --- a/src/WorkflowCore/Services/ActivityController.cs +++ b/src/WorkflowCore/Services/ActivityController.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; +using WorkflowCore.Exceptions; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -96,11 +97,14 @@ private async Task SubmitActivityResult(string token, object result) { var tokenObj = Token.Decode(token); var sub = await _subscriptionRepository.GetSubscription(tokenObj.SubscriptionId); + if (sub == null) + throw new NotFoundException(); + if (sub.ExternalToken != token) - throw new InvalidOperationException("Token mismatch"); + throw new NotFoundException("Token mismatch"); if (!await _lockProvider.AcquireLock(sub.WorkflowId, CancellationToken.None)) - throw new InvalidOperationException("Workflow is locked"); + throw new WorkflowLockedException(); try { diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 8ee703846..1f015f488 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 2.2.0 - 3.0.0.0 - 3.0.0.0 + 3.0.1 + 3.0.1.0 + 3.0.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png 3.0.0 diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 7cbcd93b4..f81abfd03 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -14,7 +14,7 @@ false false false - 2.2.0 + 3.0.0 Base package for Workflow-core peristence providers using entity framework 3.0.0.0 3.0.0.0 diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index f58aa84ef..9f12cab3b 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -177,7 +177,7 @@ public async Task TerminateSubscription(string eventSubscriptionId) public async Task GetSubscription(string eventSubscriptionId) { var result = await EventSubscriptions.FindAsync(x => x.Id == eventSubscriptionId); - return await result.FirstAsync(); + return await result.FirstOrDefaultAsync(); } public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 00b213d62..0ee5faf20 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -14,10 +14,10 @@ false false false - 2.2.0 + 3.0.1 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 3.0.0.0 - 3.0.0.0 + 3.0.1.0 + 3.0.1.0 3.0.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 48732f95d..8570745e9 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,7 +15,7 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 2.2.0 + 3.0.0 3.0.0.0 3.0.0.0 3.0.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index d5a181360..148bba34e 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,7 +15,7 @@ false false false - 2.2.0 + 3.0.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. 3.0.0.0 3.0.0.0 diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index a20f3c440..3e7ac61c7 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,7 +11,7 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 2.2.0 + 3.0.0 3.0.0.0 3.0.0 diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index 1ef23b6e6..350e61e80 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 2.2.1 + 3.0.0 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git From 6877096666f225ddb682e265be27b5482131f6f0 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 28 Dec 2019 18:17:57 -0800 Subject: [PATCH 180/462] async activity results --- src/WorkflowCore/Models/ActivityResult.cs | 1 + .../Services/ActivityController.cs | 54 ++++++++++--------- .../Services/BackgroundTasks/EventConsumer.cs | 26 ++++++++- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/WorkflowCore/Models/ActivityResult.cs b/src/WorkflowCore/Models/ActivityResult.cs index 940c07393..212ca92d8 100644 --- a/src/WorkflowCore/Models/ActivityResult.cs +++ b/src/WorkflowCore/Models/ActivityResult.cs @@ -9,6 +9,7 @@ public class ActivityResult { public enum StatusType { Success, Fail } public StatusType Status { get; set; } + public string SubscriptionId { get; set; } public object Data { get; set; } } } diff --git a/src/WorkflowCore/Services/ActivityController.cs b/src/WorkflowCore/Services/ActivityController.cs index fafa26e98..46150cad5 100644 --- a/src/WorkflowCore/Services/ActivityController.cs +++ b/src/WorkflowCore/Services/ActivityController.cs @@ -18,14 +18,16 @@ public class ActivityController : IActivityController private readonly IDistributedLockProvider _lockProvider; private readonly IDateTimeProvider _dateTimeProvider; private readonly IQueueProvider _queueProvider; + private readonly IWorkflowController _workflowController; - public ActivityController(ISubscriptionRepository subscriptionRepository, IWorkflowRepository persistenceStore, IDateTimeProvider dateTimeProvider, IDistributedLockProvider lockProvider, IQueueProvider queueProvider) + public ActivityController(ISubscriptionRepository subscriptionRepository, IWorkflowRepository persistenceStore, IWorkflowController workflowController, IDateTimeProvider dateTimeProvider, IDistributedLockProvider lockProvider, IQueueProvider queueProvider) { _persistenceStore = persistenceStore; _subscriptionRepository = subscriptionRepository; _dateTimeProvider = dateTimeProvider; _lockProvider = lockProvider; _queueProvider = queueProvider; + _workflowController = workflowController; } public async Task GetPendingActivity(string activityName, string workerId, TimeSpan? timeout = null) @@ -93,7 +95,7 @@ public async Task SubmitActivityFailure(string token, object result) }); } - private async Task SubmitActivityResult(string token, object result) + private async Task SubmitActivityResult(string token, ActivityResult result) { var tokenObj = Token.Decode(token); var sub = await _subscriptionRepository.GetSubscription(tokenObj.SubscriptionId); @@ -102,28 +104,32 @@ private async Task SubmitActivityResult(string token, object result) if (sub.ExternalToken != token) throw new NotFoundException("Token mismatch"); - - if (!await _lockProvider.AcquireLock(sub.WorkflowId, CancellationToken.None)) - throw new WorkflowLockedException(); - - try - { - var workflow = await _persistenceStore.GetWorkflowInstance(sub.WorkflowId); - var pointer = workflow.ExecutionPointers.Single(p => p.Id == sub.ExecutionPointerId); - - pointer.EventData = result; - pointer.EventPublished = true; - pointer.Active = true; - - workflow.NextExecution = 0; - await _persistenceStore.PersistWorkflow(workflow); - await _subscriptionRepository.TerminateSubscription(sub.Id); - } - finally - { - await _lockProvider.ReleaseLock(sub.WorkflowId); - await _queueProvider.QueueWork(sub.WorkflowId, QueueType.Workflow); - } + + result.SubscriptionId = sub.Id; + + await _workflowController.PublishEvent(sub.EventName, sub.EventKey, result); + + //if (!await _lockProvider.AcquireLock(sub.WorkflowId, CancellationToken.None)) + // throw new WorkflowLockedException(); + + //try + //{ + // var workflow = await _persistenceStore.GetWorkflowInstance(sub.WorkflowId); + // var pointer = workflow.ExecutionPointers.Single(p => p.Id == sub.ExecutionPointerId); + + // pointer.EventData = result; + // pointer.EventPublished = true; + // pointer.Active = true; + + // workflow.NextExecution = 0; + // await _persistenceStore.PersistWorkflow(workflow); + // await _subscriptionRepository.TerminateSubscription(sub.Id); + //} + //finally + //{ + // await _lockProvider.ReleaseLock(sub.WorkflowId); + // await _queueProvider.QueueWork(sub.WorkflowId, QueueType.Workflow); + //} } class Token diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index ed25fa9a4..06682431a 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -43,7 +43,23 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance var evt = await _eventRepository.GetEvent(itemId); if (evt.EventTime <= _datetimeProvider.UtcNow) { - var subs = await _subscriptionRepository.GetSubscriptions(evt.EventName, evt.EventKey, evt.EventTime); + IEnumerable subs = null; + if (evt.EventData is ActivityResult) + { + var activity = await _subscriptionRepository.GetSubscription((evt.EventData as ActivityResult).SubscriptionId); + if (activity == null) + { + Logger.LogWarning($"Activity already processed - {(evt.EventData as ActivityResult).SubscriptionId}"); + await _eventRepository.MarkEventProcessed(itemId); + return; + } + subs = new List() { activity }; + } + else + { + subs = await _subscriptionRepository.GetSubscriptions(evt.EventName, evt.EventKey, evt.EventTime); + } + var toQueue = new List(); var complete = true; @@ -90,7 +106,13 @@ private async Task SeedSubscription(Event evt, EventSubscription sub, List try { var workflow = await _workflowRepository.GetWorkflowInstance(sub.WorkflowId); - var pointers = workflow.ExecutionPointers.Where(p => p.EventName == sub.EventName && p.EventKey == sub.EventKey && !p.EventPublished && p.EndTime == null); + IEnumerable pointers = null; + + if (!string.IsNullOrEmpty(sub.ExecutionPointerId)) + pointers = workflow.ExecutionPointers.Where(p => p.Id == sub.ExecutionPointerId && !p.EventPublished && p.EndTime == null); + else + pointers = workflow.ExecutionPointers.Where(p => p.EventName == sub.EventName && p.EventKey == sub.EventKey && !p.EventPublished && p.EndTime == null); + foreach (var p in pointers) { p.EventData = evt.EventData; From 55a29a2643bcb5fe4b8261ccb757ce46efa277b2 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 28 Dec 2019 18:24:55 -0800 Subject: [PATCH 181/462] package versions --- src/WorkflowCore/WorkflowCore.csproj | 2 +- .../WorkflowCore.Persistence.MongoDB.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 1f015f488..3425ff328 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -20,7 +20,7 @@ 3.0.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.0.0 + 3.0.1 diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 0ee5faf20..4195aece4 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -18,7 +18,7 @@ Provides support to persist workflows running on Workflow Core to a MongoDB database. 3.0.1.0 3.0.1.0 - 3.0.0 + 3.0.1 From fc54f1934f23b6bd500ed833778b354996bab705 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 28 Dec 2019 18:33:01 -0800 Subject: [PATCH 182/462] unused dependencies --- .../Services/ActivityController.cs | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/WorkflowCore/Services/ActivityController.cs b/src/WorkflowCore/Services/ActivityController.cs index 46150cad5..e8d1d727b 100644 --- a/src/WorkflowCore/Services/ActivityController.cs +++ b/src/WorkflowCore/Services/ActivityController.cs @@ -13,20 +13,16 @@ namespace WorkflowCore.Services { public class ActivityController : IActivityController { - private readonly IWorkflowRepository _persistenceStore; private readonly ISubscriptionRepository _subscriptionRepository; private readonly IDistributedLockProvider _lockProvider; private readonly IDateTimeProvider _dateTimeProvider; - private readonly IQueueProvider _queueProvider; private readonly IWorkflowController _workflowController; - public ActivityController(ISubscriptionRepository subscriptionRepository, IWorkflowRepository persistenceStore, IWorkflowController workflowController, IDateTimeProvider dateTimeProvider, IDistributedLockProvider lockProvider, IQueueProvider queueProvider) + public ActivityController(ISubscriptionRepository subscriptionRepository, IWorkflowController workflowController, IDateTimeProvider dateTimeProvider, IDistributedLockProvider lockProvider) { - _persistenceStore = persistenceStore; _subscriptionRepository = subscriptionRepository; _dateTimeProvider = dateTimeProvider; _lockProvider = lockProvider; - _queueProvider = queueProvider; _workflowController = workflowController; } @@ -108,28 +104,6 @@ private async Task SubmitActivityResult(string token, ActivityResult result) result.SubscriptionId = sub.Id; await _workflowController.PublishEvent(sub.EventName, sub.EventKey, result); - - //if (!await _lockProvider.AcquireLock(sub.WorkflowId, CancellationToken.None)) - // throw new WorkflowLockedException(); - - //try - //{ - // var workflow = await _persistenceStore.GetWorkflowInstance(sub.WorkflowId); - // var pointer = workflow.ExecutionPointers.Single(p => p.Id == sub.ExecutionPointerId); - - // pointer.EventData = result; - // pointer.EventPublished = true; - // pointer.Active = true; - - // workflow.NextExecution = 0; - // await _persistenceStore.PersistWorkflow(workflow); - // await _subscriptionRepository.TerminateSubscription(sub.Id); - //} - //finally - //{ - // await _lockProvider.ReleaseLock(sub.WorkflowId); - // await _queueProvider.QueueWork(sub.WorkflowId, QueueType.Workflow); - //} } class Token From 24483ede4c90a9e9120a859f76083d795e3a8119 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 28 Dec 2019 18:40:30 -0800 Subject: [PATCH 183/462] reseed rule --- .../Services/BackgroundTasks/WorkflowConsumer.cs | 11 +++++++---- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 1548ddd10..f9222c715 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -92,11 +92,14 @@ private async Task SubscribeEvent(EventSubscription subscription, IPersistencePr Logger.LogDebug("Subscribing to event {0} {1} for workflow {2} step {3}", subscription.EventName, subscription.EventKey, subscription.WorkflowId, subscription.StepId); await persistenceStore.CreateEventSubscription(subscription); - var events = await persistenceStore.GetEvents(subscription.EventName, subscription.EventKey, subscription.SubscribeAsOf); - foreach (var evt in events) + if (subscription.EventName != Event.EventTypeActivity) { - await persistenceStore.MarkEventUnprocessed(evt); - await QueueProvider.QueueWork(evt, QueueType.Event); + var events = await persistenceStore.GetEvents(subscription.EventName, subscription.EventKey, subscription.SubscribeAsOf); + foreach (var evt in events) + { + await persistenceStore.MarkEventUnprocessed(evt); + await QueueProvider.QueueWork(evt, QueueType.Event); + } } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 3425ff328..429d29acc 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.0.1 - 3.0.1.0 - 3.0.1.0 + 3.0.2 + 3.0.2.0 + 3.0.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.0.1 + 3.0.2 From 580cd447b915201fa9bcac881e11187682ec36d3 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 28 Dec 2019 19:00:54 -0800 Subject: [PATCH 184/462] mongodb indexes --- .../Services/MongoPersistenceProvider.cs | 13 +++++++++++++ .../WorkflowCore.Persistence.MongoDB.csproj | 8 ++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 9f12cab3b..8edd69615 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -94,6 +94,19 @@ static void CreateIndexes(MongoPersistenceProvider instance) Builders.IndexKeys.Ascending(x => x.IsProcessed), new CreateIndexOptions {Background = true, Name = "idx_processed"})); + instance.Events.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys + .Ascending(x => x.EventName) + .Ascending(x => x.EventKey) + .Ascending(x => x.EventTime), + new CreateIndexOptions { Background = true, Name = "idx_namekey" })); + + instance.EventSubscriptions.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys + .Ascending(x => x.EventName) + .Ascending(x => x.EventKey), + new CreateIndexOptions { Background = true, Name = "idx_namekey" })); + indexesCreated = true; } } diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 4195aece4..b762cb930 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -14,11 +14,11 @@ false false false - 3.0.1 + 3.0.2 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 3.0.1.0 - 3.0.1.0 - 3.0.1 + 3.0.2.0 + 3.0.2.0 + 3.0.2 From effdd02804d31e198fafbcb1f191b2e9db7d2621 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 5 Jan 2020 08:20:19 -0800 Subject: [PATCH 185/462] Decision Branching (#486) --- ReleaseNotes/3.1.0.md | 84 +++++++++++++++ WorkflowCore.sln | 13 ++- docs/control-structures.md | 31 ++++++ docs/json-yaml.md | 66 ++++++++++++ .../Models/v1/StepSourceV1.cs | 3 +- .../Services/DefinitionLoader.cs | 22 +++- src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 4 +- src/WorkflowCore/Interface/IStepBuilder.cs | 14 +++ .../Interface/IWorkflowBuilder.cs | 7 ++ src/WorkflowCore/Models/StepOutcome.cs | 6 +- src/WorkflowCore/Primitives/Decide.cs | 16 +++ .../Services/FluentBuilders/StepBuilder.cs | 34 +++++- .../FluentBuilders/WorkflowBuilder.cs | 33 +++++- src/WorkflowCore/WorkflowCore.csproj | 9 +- .../WorkflowCore.Sample12/OutcomeWorkflow.cs | 28 +++-- src/samples/WorkflowCore.Sample12/Program.cs | 2 +- src/samples/WorkflowCore.Sample12/README.md | 46 ++++---- src/samples/WorkflowCore.Sample18/Program.cs | 4 +- test/ScratchPad/HelloWorld.json | 27 +++-- test/ScratchPad/Program.cs | 40 +++---- test/ScratchPad/ScratchPad.csproj | 1 + .../Scenarios/DecisionScenario.cs | 100 ++++++++++++++++++ .../Scenarios/StoredJsonScenario.cs | 29 ++++- .../stored-definition.json | 25 +++++ .../Scenarios/MongoDecisionScenario.cs | 19 ++++ .../ExecutionResultProcessorFixture.cs | 6 +- 26 files changed, 580 insertions(+), 89 deletions(-) create mode 100644 ReleaseNotes/3.1.0.md create mode 100644 src/WorkflowCore/Primitives/Decide.cs create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/DecisionScenario.cs create mode 100644 test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs diff --git a/ReleaseNotes/3.1.0.md b/ReleaseNotes/3.1.0.md new file mode 100644 index 000000000..613c8972d --- /dev/null +++ b/ReleaseNotes/3.1.0.md @@ -0,0 +1,84 @@ +# Workflow Core 3.1.0 + +## Decision Branching + +You can define multiple independent branches within your workflow and select one based on an expression value. + +For the fluent API, we define our branches with the `CreateBranch()` method on the workflow builder. We can then select a branch using the `Decision` step. + +This workflow will select `branch1` if the value of `data.Value1` is `one`, and `branch2` if it is `two`. +```c# +var branch1 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 1") + .Then() + .Input(step => step.Message, data => "bye from 1"); + +var branch2 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 2") + .Then() + .Input(step => step.Message, data => "bye from 2"); + + +builder + .StartWith() + .Decide(data => data.Value1) + .Branch("one", branch1) + .Branch("two", branch2); +``` + +The JSON representation would look somthing like this. + +```json +{ + "Id": "DecisionWorkflow", + "Version": 1, + "DataType": "MyApp.MyData, MyApp", + "Steps": [ + { + "Id": "decide", + "StepType": "WorkflowCore.Primitives.Decide, WorkflowCore", + "Inputs": + { + "Expression": "data.Value1" + }, + "OutcomeSteps": + { + "Print1": "\"one\"", + "Print2": "\"two\"" + } + }, + { + "Id": "Print1", + "StepType": "MyApp.PrintMessage, MyApp", + "Inputs": + { + "Message": "\"Hello from 1\"" + } + }, + { + "Id": "Print2", + "StepType": "MyApp.PrintMessage, MyApp", + "Inputs": + { + "Message": "\"Hello from 2\"" + } + } + ] +} +``` + + +## Outcomes for JSON workflows + +You can now specify `OutcomeSteps` for a step in JSON and YAML workflow definitions. + +``` +"OutcomeSteps": +{ + "<>": "<>", + "<>": "<>" +} +``` +If the outcome of a step matches a particular expression, that step would be scheduled as the next step to execute. \ No newline at end of file diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 8177c2ef9..d485c9db8 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -101,6 +101,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\2.0.0.md = ReleaseNotes\2.0.0.md ReleaseNotes\2.1.0.md = ReleaseNotes\2.1.0.md ReleaseNotes\2.1.2.md = ReleaseNotes\2.1.2.md + ReleaseNotes\3.0.0.md = ReleaseNotes\3.0.0.md + ReleaseNotes\3.1.0.md = ReleaseNotes\3.1.0.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" @@ -135,9 +137,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.Elastics EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.Redis", "test\WorkflowCore.Tests.Redis\WorkflowCore.Tests.Redis.csproj", "{78217204-B873-40B9-8875-E3925B2FBCEC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.DSL", "src\WorkflowCore.DSL\WorkflowCore.DSL.csproj", "{20B98905-08CB-4854-8E2C-A31A078383E9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.DSL", "src\WorkflowCore.DSL\WorkflowCore.DSL.csproj", "{20B98905-08CB-4854-8E2C-A31A078383E9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample18", "src\samples\WorkflowCore.Sample18\WorkflowCore.Sample18.csproj", "{5BE6D628-B9DB-4C76-AAEB-8F3800509A84}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample18", "src\samples\WorkflowCore.Sample18\WorkflowCore.Sample18.csproj", "{5BE6D628-B9DB-4C76-AAEB-8F3800509A84}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchPad\ScratchPad.csproj", "{FD7B9F06-9970-4C30-A2C0-FD7412FF620B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -341,6 +345,10 @@ Global {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Debug|Any CPU.Build.0 = Debug|Any CPU {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Release|Any CPU.ActiveCfg = Release|Any CPU {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Release|Any CPU.Build.0 = Release|Any CPU + {FD7B9F06-9970-4C30-A2C0-FD7412FF620B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD7B9F06-9970-4C30-A2C0-FD7412FF620B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD7B9F06-9970-4C30-A2C0-FD7412FF620B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD7B9F06-9970-4C30-A2C0-FD7412FF620B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -398,6 +406,7 @@ Global {78217204-B873-40B9-8875-E3925B2FBCEC} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {20B98905-08CB-4854-8E2C-A31A078383E9} = {EF47161E-E399-451C-BDE8-E92AAD3BD761} {5BE6D628-B9DB-4C76-AAEB-8F3800509A84} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} + {FD7B9F06-9970-4C30-A2C0-FD7412FF620B} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/docs/control-structures.md b/docs/control-structures.md index 0c49705b8..f79260277 100644 --- a/docs/control-structures.md +++ b/docs/control-structures.md @@ -131,3 +131,34 @@ builder ) .Then(context => Console.WriteLine("Carry on")); ``` + +### Decision Branches + +You can define multiple independent branches within your workflow and select one based on an expression value. + +For the fluent API, we define our branches with the `CreateBranch()` method on the workflow builder. We can then select a branch using the `Decide` step. + +Use the `Decide` primitive step and hook up your branches via the `Branch` method. The result of the input expression will be matched to the expressions listed via the `Branch` method, and the matching next step(s) will be scheduled to execute next. + + +This workflow will select `branch1` if the value of `data.Value1` is `one`, and `branch2` if it is `two`. +```c# +var branch1 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 1") + .Then() + .Input(step => step.Message, data => "bye from 1"); + +var branch2 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 2") + .Then() + .Input(step => step.Message, data => "bye from 2"); + + +builder + .StartWith() + .Decide(data => data.Value1) + .Branch("one", branch1) + .Branch("two", branch2); +``` diff --git a/docs/json-yaml.md b/docs/json-yaml.md index 94f9767df..3e85ed128 100644 --- a/docs/json-yaml.md +++ b/docs/json-yaml.md @@ -480,3 +480,69 @@ Do: - Id: do2 StepType: MyApp.DoSomething2, MyApp ``` + +### Decision Branches + +You can define multiple independent branches within your workflow and select one based on an expression value. +Use the `Decide` primitive step and hook up your branches via the `OutcomeSteps` property. The result of the input expression will be matched to the expressions listed in `OutcomeSteps`, and the matching next step(s) will be scheduled to execute next. + +```json +{ + "Id": "DecisionWorkflow", + "Version": 1, + "DataType": "MyApp.MyData, MyApp", + "Steps": [ + { + "Id": "decide", + "StepType": "WorkflowCore.Primitives.Decide, WorkflowCore", + "Inputs": + { + "Expression": "<>" + }, + "OutcomeSteps": + { + "Branch1": "<>", + "Branch2": "<>" + } + }, + { + "Id": "Branch1", + "StepType": "MyApp.PrintMessage, MyApp", + "Inputs": + { + "Message": "\"Hello from 1\"" + } + }, + { + "Id": "Branch2", + "StepType": "MyApp.PrintMessage, MyApp", + "Inputs": + { + "Message": "\"Hello from 2\"" + } + } + ] +} +``` + +```yaml +Id: DecisionWorkflow +Version: 1 +DataType: MyApp.MyData, MyApp +Steps: +- Id: decide + StepType: WorkflowCore.Primitives.Decide, WorkflowCore + Inputs: + Expression: <> + OutcomeSteps: + Branch1: '<>' + Branch2: '<>' +- Id: Branch1 + StepType: MyApp.PrintMessage, MyApp + Inputs: + Message: '"Hello from 1"' +- Id: Branch2 + StepType: MyApp.PrintMessage, MyApp + Inputs: + Message: '"Hello from 2"' +``` \ No newline at end of file diff --git a/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs b/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs index 350da2407..1f3c9bcaf 100644 --- a/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs +++ b/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs @@ -30,7 +30,8 @@ public class StepSourceV1 public ExpandoObject Inputs { get; set; } = new ExpandoObject(); public Dictionary Outputs { get; set; } = new Dictionary(); - + public Dictionary OutcomeSteps { get; set; } = new Dictionary(); + } } diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index 5483e0851..e235c8210 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -93,7 +93,7 @@ private WorkflowStepCollection ConvertSteps(ICollection source, Ty AttachInputs(nextStep, dataType, stepType, targetStep); AttachOutputs(nextStep, dataType, stepType, targetStep); - + if (nextStep.Do != null) { foreach (var branch in nextStep.Do) @@ -115,8 +115,7 @@ private WorkflowStepCollection ConvertSteps(ICollection source, Ty compensatables.Add(nextStep); } - if (!string.IsNullOrEmpty(nextStep.NextStepId)) - targetStep.Outcomes.Add(new StepOutcome() { ExternalNextStepId = $"{nextStep.NextStepId}" }); + AttachOutcomes(nextStep, dataType, targetStep); result.Add(targetStep); @@ -234,6 +233,23 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo } } + private void AttachOutcomes(StepSourceV1 source, Type dataType, WorkflowStep step) + { + if (!string.IsNullOrEmpty(source.NextStepId)) + step.Outcomes.Add(new StepOutcome() { ExternalNextStepId = $"{source.NextStepId}" }); + + foreach (var nextStep in source.OutcomeSteps) + { + var dataParameter = Expression.Parameter(dataType, "data"); + var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { dataParameter }, typeof(object), nextStep.Value); + step.Outcomes.Add(new StepOutcome() + { + Value = sourceExpr, + ExternalNextStepId = $"{nextStep.Key}" + }); + } + } + private Type FindType(string name) { return Type.GetType(name, true, true); diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index 4a27cf7ea..e09af788f 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -1,8 +1,8 @@ - + netstandard2.0 - 3.0.0 + 3.1.0 DSL extenstion for Workflow Core provding support for JSON and YAML workflow definitions. Daniel Gerlag diff --git a/src/WorkflowCore/Interface/IStepBuilder.cs b/src/WorkflowCore/Interface/IStepBuilder.cs index e87cfe438..a2c7e2659 100644 --- a/src/WorkflowCore/Interface/IStepBuilder.cs +++ b/src/WorkflowCore/Interface/IStepBuilder.cs @@ -73,6 +73,13 @@ public interface IStepBuilder [Obsolete] IStepOutcomeBuilder When(object outcomeValue, string label = null); + /// + /// Configure an outcome branch for this step, then wire it to another step + /// + /// + /// + IStepBuilder Branch(object outcomeValue, IStepBuilder branch) where TStep : IStepBody; + /// /// Map properties on the step to properties on the workflow data object before the step executes /// @@ -158,6 +165,13 @@ public interface IStepBuilder /// IStepBuilder Delay(Expression> period); + /// + /// Evaluate an expression and take a different path depending on the value + /// + /// Expression to evaluate for decision + /// + IStepBuilder Decide(Expression> expression); + /// /// Execute a block of steps, once for each item in a collection in a parallel foreach /// diff --git a/src/WorkflowCore/Interface/IWorkflowBuilder.cs b/src/WorkflowCore/Interface/IWorkflowBuilder.cs index 7b0b90ffb..404a359f3 100644 --- a/src/WorkflowCore/Interface/IWorkflowBuilder.cs +++ b/src/WorkflowCore/Interface/IWorkflowBuilder.cs @@ -7,6 +7,8 @@ namespace WorkflowCore.Interface { public interface IWorkflowBuilder { + List Steps { get; } + int LastStep { get; } IWorkflowBuilder UseData(); @@ -14,6 +16,8 @@ public interface IWorkflowBuilder WorkflowDefinition Build(string id, int version); void AddStep(WorkflowStep step); + + void AttachBranch(IWorkflowBuilder branch); } public interface IWorkflowBuilder : IWorkflowBuilder @@ -27,5 +31,8 @@ public interface IWorkflowBuilder : IWorkflowBuilder IEnumerable GetUpstreamSteps(int id); IWorkflowBuilder UseDefaultErrorBehavior(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null); + + IWorkflowBuilder CreateBranch(); + } } \ No newline at end of file diff --git a/src/WorkflowCore/Models/StepOutcome.cs b/src/WorkflowCore/Models/StepOutcome.cs index cf4e1a0b6..aa9268856 100644 --- a/src/WorkflowCore/Models/StepOutcome.cs +++ b/src/WorkflowCore/Models/StepOutcome.cs @@ -5,9 +5,9 @@ namespace WorkflowCore.Models { public class StepOutcome { - private Expression> _value; + private LambdaExpression _value; - public Expression> Value + public LambdaExpression Value { set { _value = value; } } @@ -23,7 +23,7 @@ public object GetValue(object data) if (_value == null) return null; - return _value.Compile().Invoke(data); + return _value.Compile().DynamicInvoke(data); } } } diff --git a/src/WorkflowCore/Primitives/Decide.cs b/src/WorkflowCore/Primitives/Decide.cs new file mode 100644 index 000000000..ec33ccaf9 --- /dev/null +++ b/src/WorkflowCore/Primitives/Decide.cs @@ -0,0 +1,16 @@ +using System; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Primitives +{ + public class Decide : StepBody + { + public object Expression { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + return ExecutionResult.Outcome(Expression); + } + } +} diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index e405c02b3..f0a1c8b2e 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -90,15 +90,33 @@ public IStepBuilder Attach(string id) public IStepOutcomeBuilder When(object outcomeValue, string label = null) { + Expression> expr = x => outcomeValue; StepOutcome result = new StepOutcome { - Value = x => outcomeValue, + Value = expr, Label = label }; Step.Outcomes.Add(result); var outcomeBuilder = new StepOutcomeBuilder(WorkflowBuilder, result); return outcomeBuilder; } + + public IStepBuilder Branch(object outcomeValue, IStepBuilder branch) where TStep : IStepBody + { + if (branch.WorkflowBuilder.Steps.Count == 0) + return this; + + WorkflowBuilder.AttachBranch(branch.WorkflowBuilder); + Expression> expr = x => outcomeValue; + + Step.Outcomes.Add(new StepOutcome + { + Value = expr, + NextStep = branch.WorkflowBuilder.Steps[0].Id + }); + + return this; + } public IStepBuilder Input(Expression> stepProperty, Expression> value) { @@ -244,6 +262,20 @@ public IStepBuilder Delay(Expression> period return stepBuilder; } + public IStepBuilder Decide(Expression> expression) + { + var newStep = new WorkflowStep(); + + Expression> inputExpr = (x => x.Expression); + newStep.Inputs.Add(new MemberMapParameter(expression, inputExpr)); + + WorkflowBuilder.AddStep(newStep); + var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); + Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + + return stepBuilder; + } + public IContainerStepBuilder ForEach(Expression> collection) { var newStep = new WorkflowStep(); diff --git a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs index 195231c10..2621f80d3 100644 --- a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs @@ -9,7 +9,9 @@ namespace WorkflowCore.Services { public class WorkflowBuilder : IWorkflowBuilder { - protected List Steps { get; set; } = new List(); + public List Steps { get; set; } = new List(); + + protected ICollection Branches { get; set; } = new List(); protected WorkflowErrorHandling DefaultErrorBehavior = WorkflowErrorHandling.Retry; @@ -56,6 +58,28 @@ private void AttachExternalIds() } } + public void AttachBranch(IWorkflowBuilder branch) + { + if (Branches.Contains(branch)) + return; + + foreach (var step in branch.Steps) + { + var oldId = step.Id; + AddStep(step); + foreach (var step2 in branch.Steps) + { + foreach (var outcome in step2.Outcomes) + { + if (outcome.NextStep == oldId) + outcome.NextStep = step.Id; + } + } + } + + Branches.Add(branch); + } + } public class WorkflowBuilder : WorkflowBuilder, IWorkflowBuilder @@ -118,6 +142,13 @@ public IWorkflowBuilder UseDefaultErrorBehavior(WorkflowErrorHandling beh DefaultErrorRetryInterval = retryInterval; return this; } + + public IWorkflowBuilder CreateBranch() + { + var result = new WorkflowBuilder(new List()); + return result; + } + } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 429d29acc..774bb3087 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.0.2 - 3.0.2.0 - 3.0.2.0 + 3.1.0 + 3.1.0.0 + 3.1.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.0.2 + 3.1.0 @@ -29,7 +29,6 @@ - diff --git a/src/samples/WorkflowCore.Sample12/OutcomeWorkflow.cs b/src/samples/WorkflowCore.Sample12/OutcomeWorkflow.cs index 42878e024..61f1f98cd 100644 --- a/src/samples/WorkflowCore.Sample12/OutcomeWorkflow.cs +++ b/src/samples/WorkflowCore.Sample12/OutcomeWorkflow.cs @@ -13,23 +13,29 @@ public class OutcomeWorkflow : IWorkflow public void Build(IWorkflowBuilder builder) { + var branch1 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 1") + .Then() + .Input(step => step.Message, data => "bye from 1"); + + var branch2 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 2") + .Then() + .Input(step => step.Message, data => "bye from 2"); + + builder .StartWith() - .Then() - .When(data => 1).Do(then => then - .StartWith() - .Input(step => step.Message, data => "Outcome was 1") - ) - .When(data => 2).Do(then => then - .StartWith() - .Input(step => step.Message, data => "Outcome was 2") - ) - .Then(); + .Decide(data => data.Value) + .Branch(1, branch1) + .Branch(2, branch2); } } public class MyData { - public int Counter { get; set; } + public int Value { get; set; } } } diff --git a/src/samples/WorkflowCore.Sample12/Program.cs b/src/samples/WorkflowCore.Sample12/Program.cs index 6a9ebe317..81f50021d 100644 --- a/src/samples/WorkflowCore.Sample12/Program.cs +++ b/src/samples/WorkflowCore.Sample12/Program.cs @@ -17,7 +17,7 @@ public static void Main(string[] args) host.Start(); Console.WriteLine("Starting workflow..."); - string workflowId = host.StartWorkflow("outcome-sample").Result; + host.StartWorkflow("outcome-sample", new MyData() { Value = 2 }); Console.ReadLine(); diff --git a/src/samples/WorkflowCore.Sample12/README.md b/src/samples/WorkflowCore.Sample12/README.md index d0b8e7629..9ecfd1809 100644 --- a/src/samples/WorkflowCore.Sample12/README.md +++ b/src/samples/WorkflowCore.Sample12/README.md @@ -1,32 +1,32 @@ -# Outcome sample +# Decision and branch sample -Illustrates how to switch different workflow paths based on a step outcome +Illustrates how to switch different workflow paths based on an expression value. -First we need a workflow step with a specific outcome. -```c# -public class DetermineSomething : StepBody -{ - public override ExecutionResult Run(IStepExecutionContext context) - { - return ExecutionResult.Outcome(2); - } -} -``` +You can define multiple independent branches within your workflow and select one based on an expression value. + +For the fluent API, we define our branches with the `CreateBranch()` method on the workflow builder. We can then select a branch using the `Decide` step. + +Use the `Decide` primitive step and hook up your branches via the `Branch` method. The result of the input expression will be matched to the expressions listed via the `Branch` method, and the matching next step(s) will be scheduled to execute next. -Then we use the .When().Do method to determine which sub-path we take. ```c# +var branch1 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 1") + .Then() + .Input(step => step.Message, data => "bye from 1"); + +var branch2 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 2") + .Then() + .Input(step => step.Message, data => "bye from 2"); + + builder .StartWith() - .Then() - .When(data => 1).Do(then => then - .StartWith() - .Input(step => step.Message, data => "Outcome was 1") - ) - .When(data => 2).Do(then => then - .StartWith() - .Input(step => step.Message, data => "Outcome was 2") - ) - .Then(); + .Decide(data => data.Value) + .Branch(1, branch1) + .Branch(2, branch2); ``` diff --git a/src/samples/WorkflowCore.Sample18/Program.cs b/src/samples/WorkflowCore.Sample18/Program.cs index 68e58af25..8bf555690 100644 --- a/src/samples/WorkflowCore.Sample18/Program.cs +++ b/src/samples/WorkflowCore.Sample18/Program.cs @@ -38,8 +38,8 @@ private static IServiceProvider ConfigureServices() { //setup dependency injection IServiceCollection services = new ServiceCollection(); - services.AddWorkflow(); - //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); + //services.AddWorkflow(); + services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); //services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); //services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;", true, true)); services.AddLogging(cfg => diff --git a/test/ScratchPad/HelloWorld.json b/test/ScratchPad/HelloWorld.json index b947071fa..1067a681c 100644 --- a/test/ScratchPad/HelloWorld.json +++ b/test/ScratchPad/HelloWorld.json @@ -7,23 +7,30 @@ { "Id": "Hello", "StepType": "ScratchPad.HelloWorld, ScratchPad", - "NextStepId": "act" + "NextStepId": "decide" }, { - "Id": "act", - "StepType": "WorkflowCore.Primitives.Activity, WorkflowCore", - "NextStepId": "Print", + "Id": "decide", + "StepType": "WorkflowCore.Primitives.Decide, WorkflowCore", "Inputs": { - "ActivityName": "\"act1\"", - "Parameters": "data.Value1" + "Expression": "data.Value1" }, - "Outputs": { "Value3": "step.Result" } + "OutcomeSteps": + { + "Print1": "\"one\"", + "Print2": "\"two\"" + } + }, + { + "Id": "Print1", + "StepType": "ScratchPad.CustomMessage, ScratchPad", + "Inputs": { "Message": "\"Hello from 1\"" } }, { - "Id": "Print", + "Id": "Print2", "StepType": "ScratchPad.CustomMessage, ScratchPad", - "Inputs": { "Message": "data.Value3" } - } + "Inputs": { "Message": "\"Hello from 2\"" } + } ] } \ No newline at end of file diff --git a/test/ScratchPad/Program.cs b/test/ScratchPad/Program.cs index ac3d981b7..a5f77fffd 100644 --- a/test/ScratchPad/Program.cs +++ b/test/ScratchPad/Program.cs @@ -22,26 +22,17 @@ public static void Main(string[] args) var host = serviceProvider.GetService(); var loader = serviceProvider.GetService(); var activityController = serviceProvider.GetService(); - //host.RegisterWorkflow(); - loader.LoadDefinition(Properties.Resources.HelloWorld, Deserializers.Json); + host.RegisterWorkflow(); + //loader.LoadDefinition(Properties.Resources.HelloWorld, Deserializers.Json); host.Start(); - host.StartWorkflow("Test02", 1, new WfData() + host.StartWorkflow("Test01", 1, new WfData() { - Value1 = "data1", + Value1 = "two", Value2 = "data2" }); - var act = activityController.GetPendingActivity("act1", "worker1", TimeSpan.FromMinutes(1)).Result; - - if (act != null) - { - Console.WriteLine("get act " + act.Token); - Console.WriteLine(act.Parameters); - - activityController.SubmitActivitySuccess(act.Token, "BOO"); - } Console.ReadLine(); @@ -63,6 +54,7 @@ private static IServiceProvider ConfigureServices() //cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); //cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); }); + services.AddWorkflowDSL(); var serviceProvider = services.BuildServiceProvider(); @@ -104,12 +96,24 @@ public class Test01Workflow : IWorkflow { public void Build(IWorkflowBuilder builder) { - builder - .StartWith() - .Activity("act1", (data) => data.Value1) - .Output(data => data.Value3, step => step.Result) + var branch1 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 1") .Then() - .Input(step => step.Message, data => data.Value3); + .Input(step => step.Message, data => "bye from 1"); + + var branch2 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 2") + .Then() + .Input(step => step.Message, data => "bye from 2"); + + + builder + .StartWith() + .Decide(data => data.Value1) + .Branch("one", branch1) + .Branch("two", branch2); } public string Id => "Test01"; diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 89556ede3..548b8fa61 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -19,6 +19,7 @@ + diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DecisionScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DecisionScenario.cs new file mode 100644 index 000000000..3ae1faa66 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DecisionScenario.cs @@ -0,0 +1,100 @@ +using System; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Primitives; +using Xunit; +using FluentAssertions; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class DecisionScenario : WorkflowTest + { + public class AddNumbers : StepBody + { + public int Input1 { get; set; } + public int Input2 { get; set; } + public int Output { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Output = (Input1 + Input2); + return ExecutionResult.Next(); + } + } + + public class SubtractNumbers : StepBody + { + public int Input1 { get; set; } + public int Input2 { get; set; } + public int Output { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Output = (Input1 - Input2); + return ExecutionResult.Next(); + } + } + + public class MyDataClass + { + public string Op { get; set; } + public int Value1 { get; set; } + public int Value2 { get; set; } + public int Value3 { get; set; } + } + + public class DecisionWorkflow : IWorkflow + { + public string Id => "DecisionWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + var addBranch = builder.CreateBranch() + .StartWith() + .Input(step => step.Input1, data => data.Value1) + .Input(step => step.Input2, data => data.Value2) + .Output(data => data.Value3, step => step.Output); + + var subtractBranch = builder.CreateBranch() + .StartWith() + .Input(step => step.Input1, data => data.Value1) + .Input(step => step.Input2, data => data.Value2) + .Output(data => data.Value3, step => step.Output); + + builder + .StartWith() + .Input(step => step.Expression, data => data.Op) + .Branch("+", addBranch) + .Branch("-", subtractBranch); + } + } + + public DecisionScenario() + { + Setup(); + } + + [Fact] + public void Scenario1() + { + var workflowId = StartWorkflow(new MyDataClass() { Op = "+", Value1 = 2, Value2 = 3 }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + GetData(workflowId).Value3.Should().Be(5); + } + + [Fact] + public void Scenario2() + { + var workflowId = StartWorkflow(new MyDataClass() { Op = "-", Value1 = 2, Value2 = 3 }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + GetData(workflowId).Value3.Should().Be(-1); + } + } +} diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs index fcc50b555..fb7d2eed1 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs @@ -17,10 +17,10 @@ public StoredJsonScenario() Setup(); } - [Fact(DisplayName = "Execute workflow from stored JSON definition")] - public void should_execute_json_workflow() + [Fact(DisplayName = "Execute branch 1")] + public void should_execute_branch1() { - var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionJson(), new CounterBoard() { Flag1 = true, Flag2 = true }); + var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionJson(), new CounterBoard() { Flag1 = true, Flag2 = true, Flag3 = true }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); var data = GetData(workflowId); @@ -32,8 +32,29 @@ public void should_execute_json_workflow() data.Counter4.Should().Be(1); data.Counter5.Should().Be(0); data.Counter6.Should().Be(1); + data.Counter7.Should().Be(1); + data.Counter8.Should().Be(0); } - + + [Fact(DisplayName = "Execute branch 2")] + public void should_execute_branch2() + { + var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionJson(), new CounterBoard() { Flag1 = true, Flag2 = true, Flag3 = false }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + var data = GetData(workflowId); + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + data.Counter1.Should().Be(1); + data.Counter2.Should().Be(1); + data.Counter3.Should().Be(1); + data.Counter4.Should().Be(1); + data.Counter5.Should().Be(0); + data.Counter6.Should().Be(1); + data.Counter7.Should().Be(0); + data.Counter8.Should().Be(1); + } + [Fact] public void should_execute_json_workflow_with_dynamic_data() { diff --git a/test/WorkflowCore.TestAssets/stored-definition.json b/test/WorkflowCore.TestAssets/stored-definition.json index e60857fe0..6beaa1169 100644 --- a/test/WorkflowCore.TestAssets/stored-definition.json +++ b/test/WorkflowCore.TestAssets/stored-definition.json @@ -63,9 +63,34 @@ }, { "Id": "Step4", + "NextStepId": "decide", "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", "Inputs": { "Value": "data.Counter6" }, "Outputs": { "Counter6": "step.Value" } + }, + { + "Id": "decide", + "StepType": "WorkflowCore.Primitives.Decide, WorkflowCore", + "Inputs": { + "Expression": "data.Flag3" + }, + "OutcomeSteps": { + "Outcome1": "true", + "Outcome2": "false" + } + }, + { + "Id": "Outcome1", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "Inputs": { "Value": "data.Counter7" }, + "Outputs": { "Counter7": "step.Value" } + }, + { + "Id": "Outcome2", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "Inputs": { "Value": "data.Counter8" }, + "Outputs": { "Counter8": "step.Value" } } + ] } \ No newline at end of file diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs new file mode 100644 index 000000000..828dda1a2 --- /dev/null +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson.Serialization; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("Mongo collection")] + public class MongoDecisionScenario : DecisionScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + } + } +} diff --git a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs index d1378e2b3..0afd27c9c 100644 --- a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs @@ -120,8 +120,10 @@ public void should_select_correct_outcomes() var pointer1 = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var pointer2 = new ExecutionPointer() { Id = "2" }; var pointer3 = new ExecutionPointer() { Id = "3" }; - var outcome1 = new StepOutcome() { NextStep = 1, Value = data => 10 }; - var outcome2 = new StepOutcome() { NextStep = 2, Value = data => 20 }; + Expression> expr1 = data => 10; + Expression> expr2 = data => 20; + var outcome1 = new StepOutcome() { NextStep = 1, Value = expr1 }; + var outcome2 = new StepOutcome() { NextStep = 2, Value = expr2 }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer1); From d16d548f61c126f397399c396ec396658db97c2d Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 11 Jan 2020 09:50:43 -0800 Subject: [PATCH 186/462] abstract step outcomes --- .../Services/DefinitionLoader.cs | 4 +- .../Interface/IExecutionPointerFactory.cs | 2 +- src/WorkflowCore/Interface/IStepOutcome.cs | 14 +++++++ .../Interface/IStepOutcomeBuilder.cs | 2 +- .../{StepOutcome.cs => ValueOutcome.cs} | 16 ++++++- src/WorkflowCore/Models/WorkflowStep.cs | 2 +- .../ErrorHandlers/CompensateHandler.cs | 2 +- .../Services/ExecutionPointerFactory.cs | 2 +- .../Services/ExecutionResultProcessor.cs | 4 +- .../Services/FluentBuilders/StepBuilder.cs | 42 +++++++++---------- .../FluentBuilders/StepOutcomeBuilder.cs | 4 +- .../Models/UserStepContainer.cs | 5 ++- .../StepBuilderExtensions.cs | 4 +- .../Services/UserTaskBuilder.cs | 2 +- .../ExecutionResultProcessorFixture.cs | 10 ++--- .../Services/WorkflowExecutorFixture.cs | 2 +- 16 files changed, 73 insertions(+), 44 deletions(-) create mode 100644 src/WorkflowCore/Interface/IStepOutcome.cs rename src/WorkflowCore/Models/{StepOutcome.cs => ValueOutcome.cs} (58%) diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index e235c8210..5bcbf4a96 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -236,13 +236,13 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo private void AttachOutcomes(StepSourceV1 source, Type dataType, WorkflowStep step) { if (!string.IsNullOrEmpty(source.NextStepId)) - step.Outcomes.Add(new StepOutcome() { ExternalNextStepId = $"{source.NextStepId}" }); + step.Outcomes.Add(new ValueOutcome() { ExternalNextStepId = $"{source.NextStepId}" }); foreach (var nextStep in source.OutcomeSteps) { var dataParameter = Expression.Parameter(dataType, "data"); var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { dataParameter }, typeof(object), nextStep.Value); - step.Outcomes.Add(new StepOutcome() + step.Outcomes.Add(new ValueOutcome() { Value = sourceExpr, ExternalNextStepId = $"{nextStep.Key}" diff --git a/src/WorkflowCore/Interface/IExecutionPointerFactory.cs b/src/WorkflowCore/Interface/IExecutionPointerFactory.cs index 32a020eae..d23ee98fe 100644 --- a/src/WorkflowCore/Interface/IExecutionPointerFactory.cs +++ b/src/WorkflowCore/Interface/IExecutionPointerFactory.cs @@ -6,7 +6,7 @@ public interface IExecutionPointerFactory { ExecutionPointer BuildGenesisPointer(WorkflowDefinition def); ExecutionPointer BuildCompensationPointer(WorkflowDefinition def, ExecutionPointer pointer, ExecutionPointer exceptionPointer, int compensationStepId); - ExecutionPointer BuildNextPointer(WorkflowDefinition def, ExecutionPointer pointer, StepOutcome outcomeTarget); + ExecutionPointer BuildNextPointer(WorkflowDefinition def, ExecutionPointer pointer, IStepOutcome outcomeTarget); ExecutionPointer BuildChildPointer(WorkflowDefinition def, ExecutionPointer pointer, int childDefinitionId, object branch); } } \ No newline at end of file diff --git a/src/WorkflowCore/Interface/IStepOutcome.cs b/src/WorkflowCore/Interface/IStepOutcome.cs new file mode 100644 index 000000000..368371006 --- /dev/null +++ b/src/WorkflowCore/Interface/IStepOutcome.cs @@ -0,0 +1,14 @@ +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface IStepOutcome + { + string ExternalNextStepId { get; set; } + string Label { get; set; } + int NextStep { get; set; } + + bool Matches(object data); + bool Matches(ExecutionResult executionResult, object data); + } +} \ No newline at end of file diff --git a/src/WorkflowCore/Interface/IStepOutcomeBuilder.cs b/src/WorkflowCore/Interface/IStepOutcomeBuilder.cs index a9c6ca866..0145ca1db 100644 --- a/src/WorkflowCore/Interface/IStepOutcomeBuilder.cs +++ b/src/WorkflowCore/Interface/IStepOutcomeBuilder.cs @@ -8,7 +8,7 @@ public interface IStepOutcomeBuilder { IWorkflowBuilder WorkflowBuilder { get; } - StepOutcome Outcome { get; } + ValueOutcome Outcome { get; } IStepBuilder Then(Action> stepSetup = null) where TStep : IStepBody; diff --git a/src/WorkflowCore/Models/StepOutcome.cs b/src/WorkflowCore/Models/ValueOutcome.cs similarity index 58% rename from src/WorkflowCore/Models/StepOutcome.cs rename to src/WorkflowCore/Models/ValueOutcome.cs index aa9268856..a6e4f2fb2 100644 --- a/src/WorkflowCore/Models/StepOutcome.cs +++ b/src/WorkflowCore/Models/ValueOutcome.cs @@ -1,9 +1,10 @@ using System; using System.Linq.Expressions; +using WorkflowCore.Interface; namespace WorkflowCore.Models { - public class StepOutcome + public class ValueOutcome : IStepOutcome { private LambdaExpression _value; @@ -11,13 +12,23 @@ public LambdaExpression Value { set { _value = value; } } - + public int NextStep { get; set; } public string Label { get; set; } public string ExternalNextStepId { get; set; } + public bool Matches(ExecutionResult executionResult, object data) + { + return object.Equals(GetValue(data), executionResult.OutcomeValue) || GetValue(data) == null; + } + + public bool Matches(object data) + { + return GetValue(data) == null; + } + public object GetValue(object data) { if (_value == null) @@ -25,5 +36,6 @@ public object GetValue(object data) return _value.Compile().DynamicInvoke(data); } + } } diff --git a/src/WorkflowCore/Models/WorkflowStep.cs b/src/WorkflowCore/Models/WorkflowStep.cs index 73fc37b77..08ad1c525 100644 --- a/src/WorkflowCore/Models/WorkflowStep.cs +++ b/src/WorkflowCore/Models/WorkflowStep.cs @@ -18,7 +18,7 @@ public abstract class WorkflowStep public virtual List Children { get; set; } = new List(); - public virtual List Outcomes { get; set; } = new List(); + public virtual List Outcomes { get; set; } = new List(); public virtual List Inputs { get; set; } = new List(); diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index 1f3142610..12b62342a 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -72,7 +72,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP if (resume) { - foreach (var outcomeTarget in scopeStep.Outcomes.Where(x => x.GetValue(workflow.Data) == null)) + foreach (var outcomeTarget in scopeStep.Outcomes.Where(x => x.Matches(workflow.Data))) workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, scopePointer, outcomeTarget)); } } diff --git a/src/WorkflowCore/Services/ExecutionPointerFactory.cs b/src/WorkflowCore/Services/ExecutionPointerFactory.cs index 0ed6fe553..65f5a2f8f 100644 --- a/src/WorkflowCore/Services/ExecutionPointerFactory.cs +++ b/src/WorkflowCore/Services/ExecutionPointerFactory.cs @@ -21,7 +21,7 @@ public ExecutionPointer BuildGenesisPointer(WorkflowDefinition def) }; } - public ExecutionPointer BuildNextPointer(WorkflowDefinition def, ExecutionPointer pointer, StepOutcome outcomeTarget) + public ExecutionPointer BuildNextPointer(WorkflowDefinition def, ExecutionPointer pointer, IStepOutcome outcomeTarget) { var nextId = GenerateId(); return new ExecutionPointer() diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index 5b45b2a3b..448506823 100755 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -61,8 +61,8 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition pointer.Active = false; pointer.EndTime = _datetimeProvider.UtcNow; pointer.Status = PointerStatus.Complete; - - foreach (var outcomeTarget in step.Outcomes.Where(x => object.Equals(x.GetValue(workflow.Data), result.OutcomeValue) || x.GetValue(workflow.Data) == null)) + + foreach (var outcomeTarget in step.Outcomes.Where(x => x.Matches(result, workflow.Data))) { workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, pointer, outcomeTarget)); } diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index f0a1c8b2e..a15c52966 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -45,7 +45,7 @@ public IStepBuilder Then(Action> } newStep.Name = newStep.Name ?? typeof(TStep).Name; - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -53,7 +53,7 @@ public IStepBuilder Then(Action> public IStepBuilder Then(IStepBuilder newStep) where TStep : IStepBody { - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Step.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Step.Id }); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep.Step); return stepBuilder; } @@ -64,7 +64,7 @@ public IStepBuilder Then(Func(WorkflowBuilder, newStep); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -74,13 +74,13 @@ public IStepBuilder Then(Action bo WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); stepBuilder.Input(x => x.Body, x => body); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } public IStepBuilder Attach(string id) { - Step.Outcomes.Add(new StepOutcome() + Step.Outcomes.Add(new ValueOutcome() { ExternalNextStepId = id }); @@ -91,7 +91,7 @@ public IStepBuilder Attach(string id) public IStepOutcomeBuilder When(object outcomeValue, string label = null) { Expression> expr = x => outcomeValue; - StepOutcome result = new StepOutcome + ValueOutcome result = new ValueOutcome { Value = expr, Label = label @@ -109,7 +109,7 @@ public IStepBuilder Branch(object outcomeValue, IStepBu WorkflowBuilder.AttachBranch(branch.WorkflowBuilder); Expression> expr = x => outcomeValue; - Step.Outcomes.Add(new StepOutcome + Step.Outcomes.Add(new ValueOutcome { Value = expr, NextStep = branch.WorkflowBuilder.Steps[0].Id @@ -169,7 +169,7 @@ public IStepBuilder WaitFor(string eventName, Expression step.EffectiveDate, effectiveDate); } - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -188,7 +188,7 @@ public IStepBuilder WaitFor(string eventName, Expression step.EffectiveDate, effectiveDate); } - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -244,7 +244,7 @@ public IStepBuilder EndWorkflow() { EndStep newStep = new EndStep(); WorkflowBuilder.AddStep(newStep); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return this; } @@ -257,7 +257,7 @@ public IStepBuilder Delay(Expression> period WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -271,7 +271,7 @@ public IStepBuilder Decide(Expression> expres WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -286,7 +286,7 @@ public IContainerStepBuilder ForEach(Expression(WorkflowBuilder, newStep); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -301,7 +301,7 @@ public IContainerStepBuilder While(Expression(WorkflowBuilder, newStep); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -316,7 +316,7 @@ public IContainerStepBuilder If(Expression> con WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -333,7 +333,7 @@ public IContainerStepBuilder When(Expression(); WorkflowBuilder.AddStep(switchStep); - Step.Outcomes.Add(new StepOutcome() + Step.Outcomes.Add(new ValueOutcome() { NextStep = switchStep.Id, Label = label @@ -357,7 +357,7 @@ public IStepBuilder Saga(Action> builde var newStep = new SagaContainer(); WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); builder.Invoke(WorkflowBuilder); stepBuilder.Step.Children.Add(stepBuilder.Step.Id + 1); //TODO: make more elegant @@ -371,7 +371,7 @@ public IParallelStepBuilder Parallel() WorkflowBuilder.AddStep(newStep); var stepBuilder = new ParallelStepBuilder(WorkflowBuilder, newBuilder, newBuilder); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -384,7 +384,7 @@ public IContainerStepBuilder Schedule(Expression(WorkflowBuilder, newStep, this); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -401,7 +401,7 @@ public IContainerStepBuilder Recur(Expression(WorkflowBuilder, newStep, this); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -485,7 +485,7 @@ public IStepBuilder Activity(string activityName, Expression step.EffectiveDate, effectiveDate); - Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } } diff --git a/src/WorkflowCore/Services/FluentBuilders/StepOutcomeBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepOutcomeBuilder.cs index 64f244aaa..1d654152a 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepOutcomeBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepOutcomeBuilder.cs @@ -8,9 +8,9 @@ namespace WorkflowCore.Services public class StepOutcomeBuilder : IStepOutcomeBuilder { public IWorkflowBuilder WorkflowBuilder { get; private set; } - public StepOutcome Outcome { get; private set; } + public ValueOutcome Outcome { get; private set; } - public StepOutcomeBuilder(IWorkflowBuilder workflowBuilder, StepOutcome outcome) + public StepOutcomeBuilder(IWorkflowBuilder workflowBuilder, ValueOutcome outcome) { WorkflowBuilder = workflowBuilder; Outcome = outcome; diff --git a/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs b/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs index 9d308b7b5..901bd28b7 100644 --- a/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs +++ b/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs @@ -28,7 +28,10 @@ public override ExecutionPipelineDirective InitForExecution(WorkflowExecutorResu Dictionary userOptions = new Dictionary(); foreach (var outcome in Outcomes) { - userOptions[outcome.Label ?? Convert.ToString(outcome.GetValue(workflow.Data) ?? "Proceed")] = outcome.GetValue(workflow.Data); + if (outcome is ValueOutcome) + { + userOptions[outcome.Label ?? Convert.ToString((outcome as ValueOutcome).GetValue(workflow.Data) ?? "Proceed")] = (outcome as ValueOutcome).GetValue(workflow.Data); + } } executionPointer.ExtensionAttributes["UserOptions"] = userOptions; diff --git a/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs b/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs index 8221c5da9..44e9e1ce7 100644 --- a/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs +++ b/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs @@ -30,7 +30,7 @@ public static IStepBuilder UserStep(this ISte stepSetup.Invoke(stepBuilder); newStep.Name = newStep.Name ?? typeof(UserStepContainer).Name; - builder.Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + builder.Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } @@ -64,7 +64,7 @@ public static IUserTaskBuilder UserTask(this IStepBuild stepSetup.Invoke(stepBuilder); newStep.Name = newStep.Name ?? typeof(UserTask).Name; - builder.Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id }); + builder.Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); return stepBuilder; } diff --git a/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs b/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs index 175182bf7..ce241ab52 100644 --- a/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs +++ b/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs @@ -53,7 +53,7 @@ public IUserTaskBuilder WithEscalation(Expression> var lastStep = WorkflowBuilder.LastStep; action.Invoke(WorkflowBuilder); if (WorkflowBuilder.LastStep > lastStep) - newStep.Outcomes.Add(new StepOutcome() { NextStep = lastStep + 1 }); + newStep.Outcomes.Add(new ValueOutcome() { NextStep = lastStep + 1 }); } return this; diff --git a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs index 0afd27c9c..557abdb5a 100644 --- a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs @@ -50,13 +50,13 @@ public void should_advance_workflow() var definition = new WorkflowDefinition(); var pointer1 = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var pointer2 = new ExecutionPointer() { Id = "2" }; - var outcome = new StepOutcome() { NextStep = 1 }; + var outcome = new ValueOutcome() { NextStep = 1 }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer1); var result = ExecutionResult.Next(); - A.CallTo(() => step.Outcomes).Returns(new List() { outcome }); + A.CallTo(() => step.Outcomes).Returns(new List() { outcome }); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome)).Returns(pointer2); //act @@ -122,14 +122,14 @@ public void should_select_correct_outcomes() var pointer3 = new ExecutionPointer() { Id = "3" }; Expression> expr1 = data => 10; Expression> expr2 = data => 20; - var outcome1 = new StepOutcome() { NextStep = 1, Value = expr1 }; - var outcome2 = new StepOutcome() { NextStep = 2, Value = expr2 }; + var outcome1 = new ValueOutcome() { NextStep = 1, Value = expr1 }; + var outcome2 = new ValueOutcome() { NextStep = 2, Value = expr2 }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer1); var result = ExecutionResult.Outcome(20); - A.CallTo(() => step.Outcomes).Returns(new List() { outcome1, outcome2 }); + A.CallTo(() => step.Outcomes).Returns(new List() { outcome1, outcome2 }); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome1)).Returns(pointer2); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome2)).Returns(pointer3); diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 6d3f04499..6c99586f6 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -348,7 +348,7 @@ private WorkflowStep BuildFakeStep(IStepBody stepBody, List inpu A.CallTo(() => result.ConstructBody(ServiceProvider)).Returns(stepBody); A.CallTo(() => result.Inputs).Returns(inputs); A.CallTo(() => result.Outputs).Returns(outputs); - A.CallTo(() => result.Outcomes).Returns(new List()); + A.CallTo(() => result.Outcomes).Returns(new List()); A.CallTo(() => result.InitForExecution(A.Ignored, A.Ignored, A.Ignored, A.Ignored)).Returns(ExecutionPipelineDirective.Next); A.CallTo(() => result.BeforeExecute(A.Ignored, A.Ignored, A.Ignored, A.Ignored)).Returns(ExecutionPipelineDirective.Next); return result; From f6365f97188db78623cf972ba5f448e642020517 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 11 Jan 2020 09:52:35 -0800 Subject: [PATCH 187/462] bump versions --- src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 2 +- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- .../WorkflowCore.Users/WorkflowCore.Users.csproj | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index e09af788f..ac40a28ec 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 3.1.0 + 3.1.1 DSL extenstion for Workflow Core provding support for JSON and YAML workflow definitions. Daniel Gerlag diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 774bb3087..15791ab3a 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.1.0 - 3.1.0.0 - 3.1.0.0 + 3.1.1 + 3.1.1.0 + 3.1.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.1.0 + 3.1.1 diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 4a6ea76fc..742f848f5 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -15,9 +15,9 @@ false false Provides extensions for Workflow Core to enable human workflows. - 2.0.0 - 2.0.0.0 - 2.0.0.0 + 2.1.0 + 2.1.0.0 + 2.1.0.0 From 215bc71152b91c42b6f832217e012d00c57520b4 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 11 Jan 2020 10:28:38 -0800 Subject: [PATCH 188/462] expression outcome --- src/WorkflowCore/Interface/IStepBuilder.cs | 7 ++++ src/WorkflowCore/Models/ExpressionOutcome.cs | 34 +++++++++++++++++++ .../Services/FluentBuilders/StepBuilder.cs | 15 ++++++++ test/ScratchPad/Program.cs | 4 +-- 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/WorkflowCore/Models/ExpressionOutcome.cs diff --git a/src/WorkflowCore/Interface/IStepBuilder.cs b/src/WorkflowCore/Interface/IStepBuilder.cs index a2c7e2659..db28b5887 100644 --- a/src/WorkflowCore/Interface/IStepBuilder.cs +++ b/src/WorkflowCore/Interface/IStepBuilder.cs @@ -80,6 +80,13 @@ public interface IStepBuilder /// IStepBuilder Branch(object outcomeValue, IStepBuilder branch) where TStep : IStepBody; + /// + /// Configure an outcome branch for this step, then wire it to another step + /// + /// + /// + IStepBuilder Branch(Expression> outcomeExpression, IStepBuilder branch) where TStep : IStepBody; + /// /// Map properties on the step to properties on the workflow data object before the step executes /// diff --git a/src/WorkflowCore/Models/ExpressionOutcome.cs b/src/WorkflowCore/Models/ExpressionOutcome.cs new file mode 100644 index 000000000..6d30998a0 --- /dev/null +++ b/src/WorkflowCore/Models/ExpressionOutcome.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq.Expressions; +using WorkflowCore.Interface; + +namespace WorkflowCore.Models +{ + public class ExpressionOutcome : IStepOutcome + { + private readonly Func _func; + + + public int NextStep { get; set; } + + public string Label { get; set; } + + public string ExternalNextStepId { get; set; } + + public ExpressionOutcome(Expression> expression) + { + _func = expression.Compile(); + } + + public bool Matches(ExecutionResult executionResult, object data) + { + return _func((TData)data, executionResult.OutcomeValue); + } + + public bool Matches(object data) + { + return _func((TData)data, null); + } + + } +} diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index a15c52966..93f965eba 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -118,6 +118,21 @@ public IStepBuilder Branch(object outcomeValue, IStepBu return this; } + public IStepBuilder Branch(Expression> outcomeExpression, IStepBuilder branch) where TStep : IStepBody + { + if (branch.WorkflowBuilder.Steps.Count == 0) + return this; + + WorkflowBuilder.AttachBranch(branch.WorkflowBuilder); + + Step.Outcomes.Add(new ExpressionOutcome(outcomeExpression) + { + NextStep = branch.WorkflowBuilder.Steps[0].Id + }); + + return this; + } + public IStepBuilder Input(Expression> stepProperty, Expression> value) { Step.Inputs.Add(new MemberMapParameter(value, stepProperty)); diff --git a/test/ScratchPad/Program.cs b/test/ScratchPad/Program.cs index a5f77fffd..93df41ff5 100644 --- a/test/ScratchPad/Program.cs +++ b/test/ScratchPad/Program.cs @@ -112,8 +112,8 @@ public void Build(IWorkflowBuilder builder) builder .StartWith() .Decide(data => data.Value1) - .Branch("one", branch1) - .Branch("two", branch2); + .Branch((data, outcome) => data.Value1 == "one", branch1) + .Branch((data, outcome) => data.Value1 == "two", branch2); } public string Id => "Test01"; From f0e3e04ba70fade2c65c4e0b4abbf530dea87c59 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 11 Jan 2020 20:12:50 -0800 Subject: [PATCH 189/462] wip --- ReleaseNotes/3.1.0.md | 22 +++++++++---------- docs/control-structures.md | 9 ++++---- docs/json-yaml.md | 10 +++------ .../Models/v1/StepSourceV1.cs | 2 +- .../Services/DefinitionLoader.cs | 16 ++++++++------ src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 2 +- src/WorkflowCore/WorkflowCore.csproj | 8 +++---- test/ScratchPad/HelloWorld.json | 10 +++------ test/ScratchPad/Program.cs | 6 ++--- .../stored-definition.json | 9 +++----- 10 files changed, 41 insertions(+), 53 deletions(-) diff --git a/ReleaseNotes/3.1.0.md b/ReleaseNotes/3.1.0.md index 613c8972d..5c1b6c008 100644 --- a/ReleaseNotes/3.1.0.md +++ b/ReleaseNotes/3.1.0.md @@ -4,7 +4,9 @@ You can define multiple independent branches within your workflow and select one based on an expression value. -For the fluent API, we define our branches with the `CreateBranch()` method on the workflow builder. We can then select a branch using the `Decision` step. +For the fluent API, we define our branches with the `CreateBranch()` method on the workflow builder. We can then select a branch using the `Branch` method. + +The select expressions will be matched to the branch listed via the `Branch` method, and the matching next step(s) will be scheduled to execute next. This workflow will select `branch1` if the value of `data.Value1` is `one`, and `branch2` if it is `two`. ```c# @@ -24,8 +26,8 @@ var branch2 = builder.CreateBranch() builder .StartWith() .Decide(data => data.Value1) - .Branch("one", branch1) - .Branch("two", branch2); + .Branch((data, outcome) => data.Value1 == "one", branch1) + .Branch((data, outcome) => data.Value1 == "two", branch2); ``` The JSON representation would look somthing like this. @@ -38,15 +40,11 @@ The JSON representation would look somthing like this. "Steps": [ { "Id": "decide", - "StepType": "WorkflowCore.Primitives.Decide, WorkflowCore", - "Inputs": - { - "Expression": "data.Value1" - }, - "OutcomeSteps": + "StepType": "...", + "SelectNextStep": { - "Print1": "\"one\"", - "Print2": "\"two\"" + "Print1": "data.Value1 == \"one\"", + "Print2": "data.Value1 == \"two\"" } }, { @@ -75,7 +73,7 @@ The JSON representation would look somthing like this. You can now specify `OutcomeSteps` for a step in JSON and YAML workflow definitions. ``` -"OutcomeSteps": +"SelectNextStep": { "<>": "<>", "<>": "<>" diff --git a/docs/control-structures.md b/docs/control-structures.md index f79260277..0f1503c7f 100644 --- a/docs/control-structures.md +++ b/docs/control-structures.md @@ -136,10 +136,9 @@ builder You can define multiple independent branches within your workflow and select one based on an expression value. -For the fluent API, we define our branches with the `CreateBranch()` method on the workflow builder. We can then select a branch using the `Decide` step. - -Use the `Decide` primitive step and hook up your branches via the `Branch` method. The result of the input expression will be matched to the expressions listed via the `Branch` method, and the matching next step(s) will be scheduled to execute next. +For the fluent API, we define our branches with the `CreateBranch()` method on the workflow builder. We can then select a branch using the `Branch` method. +The select expressions will be matched to the branch listed via the `Branch` method, and the matching next step(s) will be scheduled to execute next. This workflow will select `branch1` if the value of `data.Value1` is `one`, and `branch2` if it is `two`. ```c# @@ -159,6 +158,6 @@ var branch2 = builder.CreateBranch() builder .StartWith() .Decide(data => data.Value1) - .Branch("one", branch1) - .Branch("two", branch2); + .Branch((data, outcome) => data.Value1 == "one", branch1) + .Branch((data, outcome) => data.Value1 == "two", branch2); ``` diff --git a/docs/json-yaml.md b/docs/json-yaml.md index 3e85ed128..acbf0ef2d 100644 --- a/docs/json-yaml.md +++ b/docs/json-yaml.md @@ -484,7 +484,7 @@ Do: ### Decision Branches You can define multiple independent branches within your workflow and select one based on an expression value. -Use the `Decide` primitive step and hook up your branches via the `OutcomeSteps` property. The result of the input expression will be matched to the expressions listed in `OutcomeSteps`, and the matching next step(s) will be scheduled to execute next. +Hook up your branches via the `SelectNextStep` property, instead of a `NextStepId`. The expressions will be matched to the step Ids listed in `SelectNextStep`, and the matching next step(s) will be scheduled to execute next. ```json { @@ -494,12 +494,8 @@ Use the `Decide` primitive step and hook up your branches via the `OutcomeSteps` "Steps": [ { "Id": "decide", - "StepType": "WorkflowCore.Primitives.Decide, WorkflowCore", - "Inputs": - { - "Expression": "<>" - }, - "OutcomeSteps": + "StepType": "...", + "SelectNextStep": { "Branch1": "<>", "Branch2": "<>" diff --git a/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs b/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs index 1f3c9bcaf..82969d765 100644 --- a/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs +++ b/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs @@ -31,7 +31,7 @@ public class StepSourceV1 public Dictionary Outputs { get; set; } = new Dictionary(); - public Dictionary OutcomeSteps { get; set; } = new Dictionary(); + public Dictionary SelectNextStep { get; set; } = new Dictionary(); } } diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index 5bcbf4a96..101b2f5b2 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -237,14 +237,16 @@ private void AttachOutcomes(StepSourceV1 source, Type dataType, WorkflowStep ste { if (!string.IsNullOrEmpty(source.NextStepId)) step.Outcomes.Add(new ValueOutcome() { ExternalNextStepId = $"{source.NextStepId}" }); - - foreach (var nextStep in source.OutcomeSteps) - { - var dataParameter = Expression.Parameter(dataType, "data"); - var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { dataParameter }, typeof(object), nextStep.Value); - step.Outcomes.Add(new ValueOutcome() + + var dataParameter = Expression.Parameter(dataType, "data"); + var outcomeParameter = Expression.Parameter(typeof(object), "outcome"); + + foreach (var nextStep in source.SelectNextStep) + { + var sourceDelegate = DynamicExpressionParser.ParseLambda(new[] { dataParameter, outcomeParameter }, typeof(object), nextStep.Value).Compile(); + Expression> sourceExpr = (data, outcome) => System.Convert.ToBoolean(sourceDelegate.DynamicInvoke(data, outcome)); + step.Outcomes.Add(new ExpressionOutcome(sourceExpr) { - Value = sourceExpr, ExternalNextStepId = $"{nextStep.Key}" }); } diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index ac40a28ec..98c8bedae 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 3.1.1 + 3.1.2 DSL extenstion for Workflow Core provding support for JSON and YAML workflow definitions. Daniel Gerlag diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 15791ab3a..cdb0e0628 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.1.1 - 3.1.1.0 - 3.1.1.0 + 3.1.2 + 3.1.2.0 + 3.1.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.1.1 + 3.1.2 diff --git a/test/ScratchPad/HelloWorld.json b/test/ScratchPad/HelloWorld.json index 1067a681c..2e7fff6bf 100644 --- a/test/ScratchPad/HelloWorld.json +++ b/test/ScratchPad/HelloWorld.json @@ -12,14 +12,10 @@ { "Id": "decide", "StepType": "WorkflowCore.Primitives.Decide, WorkflowCore", - "Inputs": + "SelectNextStep": { - "Expression": "data.Value1" - }, - "OutcomeSteps": - { - "Print1": "\"one\"", - "Print2": "\"two\"" + "Print1": "data.Value1 == \"one\"", + "Print2": "data.Value1 == \"two\"" } }, { diff --git a/test/ScratchPad/Program.cs b/test/ScratchPad/Program.cs index 93df41ff5..489a4c086 100644 --- a/test/ScratchPad/Program.cs +++ b/test/ScratchPad/Program.cs @@ -22,12 +22,12 @@ public static void Main(string[] args) var host = serviceProvider.GetService(); var loader = serviceProvider.GetService(); var activityController = serviceProvider.GetService(); - host.RegisterWorkflow(); - //loader.LoadDefinition(Properties.Resources.HelloWorld, Deserializers.Json); + //host.RegisterWorkflow(); + loader.LoadDefinition(Properties.Resources.HelloWorld, Deserializers.Json); host.Start(); - host.StartWorkflow("Test01", 1, new WfData() + host.StartWorkflow("Test02", 1, new WfData() { Value1 = "two", Value2 = "data2" diff --git a/test/WorkflowCore.TestAssets/stored-definition.json b/test/WorkflowCore.TestAssets/stored-definition.json index 6beaa1169..56f36ac5e 100644 --- a/test/WorkflowCore.TestAssets/stored-definition.json +++ b/test/WorkflowCore.TestAssets/stored-definition.json @@ -71,12 +71,9 @@ { "Id": "decide", "StepType": "WorkflowCore.Primitives.Decide, WorkflowCore", - "Inputs": { - "Expression": "data.Flag3" - }, - "OutcomeSteps": { - "Outcome1": "true", - "Outcome2": "false" + "SelectNextStep": { + "Outcome1": "data.Flag3", + "Outcome2": "!data.Flag3" } }, { From 5d494503aac019a28fa8697a2f13e82cc8e75340 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 14 Jan 2020 07:48:20 -0800 Subject: [PATCH 190/462] docs --- .gitignore | 1 + docs/activities.md | 40 +++- docs/control-structures.md | 383 +++++++++++++++++++++++++++++++--- docs/error-handling.md | 25 ++- docs/external-events.md | 37 ++++ docs/json-yaml.md | 411 +++---------------------------------- 6 files changed, 484 insertions(+), 413 deletions(-) diff --git a/.gitignore b/.gitignore index 77a8c2951..085776751 100644 --- a/.gitignore +++ b/.gitignore @@ -246,3 +246,4 @@ ModelManifest.xml *.migration_in_place_backup .idea/.idea.WorkflowCore/.idea riderModule.iml +.DS_Store diff --git a/docs/activities.md b/docs/activities.md index d458d81f2..433555d9a 100644 --- a/docs/activities.md +++ b/docs/activities.md @@ -7,7 +7,6 @@ In this example the workflow will wait for `activity-1`, before proceeding. It Then we create a worker to process the queue of activity items. It uses the `GetPendingActivity` method to get an activity and the data that a workflow is waiting for. - ```C# public class ActivityWorkflow : IWorkflow { @@ -47,4 +46,41 @@ The JSON representation of this step would look like this }, "Outputs": { "Value2": "step.Result" } } -``` \ No newline at end of file +``` + +## JSON / YAML API + +The `Activity` step can be configured using inputs as follows + +| Field | Description | +| ---------------------- | --------------------------- | +| CancelCondition | Optional expression to specify a cancel condition | +| Inputs.ActivityName | Expression to specify the activity name | +| Inputs.Parameters | Expression to specify the parameters to pass the activity worker | +| Inputs.EffectiveDate | Optional expression to specify the effective date | + + +```json +{ + "Id": "MyActivityStep", + "StepType": "WorkflowCore.Primitives.Activity, WorkflowCore", + "NextStepId": "...", + "CancelCondition": "...", + "Inputs": { + "ActivityName": "\"my-activity\"", + "Parameters": "data.SomeValue" + } +} +``` +```yaml +Id: MyActivityStep +StepType: WorkflowCore.Primitives.Activity, WorkflowCore +NextStepId: "..." +CancelCondition: "..." +Inputs: + ActivityName: '"my-activity"' + EventKey: '"Key1"' + Parameters: data.SomeValue + +``` + diff --git a/docs/control-structures.md b/docs/control-structures.md index 0f1503c7f..a16ee2478 100644 --- a/docs/control-structures.md +++ b/docs/control-structures.md @@ -1,9 +1,105 @@ # Control Structures +## Decision Branches + +You can define multiple independent branches within your workflow and select one based on an expression value. + +#### Fluent API + +For the fluent API, we define our branches with the `CreateBranch()` method on the workflow builder. We can then select a branch using the `Branch` method. + +The select expressions will be matched to the branch listed via the `Branch` method, and the matching next step(s) will be scheduled to execute next. Matching multiple next steps will result in parallel branches running. + +This workflow will select `branch1` if the value of `data.Value1` is `one`, and `branch2` if it is `two`. +```c# +var branch1 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 1") + .Then() + .Input(step => step.Message, data => "bye from 1"); + +var branch2 = builder.CreateBranch() + .StartWith() + .Input(step => step.Message, data => "hi from 2") + .Then() + .Input(step => step.Message, data => "bye from 2"); + + +builder + .StartWith() + .Decide(data => data.Value1) + .Branch((data, outcome) => data.Value1 == "one", branch1) + .Branch((data, outcome) => data.Value1 == "two", branch2); +``` + +#### JSON / YAML API + +Hook up your branches via the `SelectNextStep` property, instead of a `NextStepId`. The expressions will be matched to the step Ids listed in `SelectNextStep`, and the matching next step(s) will be scheduled to execute next. + +```json +{ + "Id": "DecisionWorkflow", + "Version": 1, + "DataType": "MyApp.MyData, MyApp", + "Steps": [ + { + "Id": "decide", + "StepType": "...", + "SelectNextStep": + { + "Branch1": "<>", + "Branch2": "<>" + } + }, + { + "Id": "Branch1", + "StepType": "MyApp.PrintMessage, MyApp", + "Inputs": + { + "Message": "\"Hello from 1\"" + } + }, + { + "Id": "Branch2", + "StepType": "MyApp.PrintMessage, MyApp", + "Inputs": + { + "Message": "\"Hello from 2\"" + } + } + ] +} +``` + +```yaml +Id: DecisionWorkflow +Version: 1 +DataType: MyApp.MyData, MyApp +Steps: +- Id: decide + StepType: WorkflowCore.Primitives.Decide, WorkflowCore + Inputs: + Expression: <> + OutcomeSteps: + Branch1: '<>' + Branch2: '<>' +- Id: Branch1 + StepType: MyApp.PrintMessage, MyApp + Inputs: + Message: '"Hello from 1"' +- Id: Branch2 + StepType: MyApp.PrintMessage, MyApp + Inputs: + Message: '"Hello from 2"' +``` + + ## Parallel ForEach Use the .ForEach method to start a parallel for loop +#### Fluent API + ```C# public class ForEachWorkflow : IWorkflow { @@ -24,10 +120,48 @@ public class ForEachWorkflow : IWorkflow } ``` +#### JSON / YAML API + +```json +{ + "Id": "MyForEachStep", + "StepType": "WorkflowCore.Primitives.ForEach, WorkflowCore", + "NextStepId": "...", + "Inputs": { "Collection": "<>" }, + "Do": [[ + { + "Id": "do1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "do2" + }, + { + "Id": "do2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ]] +} +``` +```yaml +Id: MyForEachStep +StepType: WorkflowCore.Primitives.ForEach, WorkflowCore +NextStepId: "..." +Inputs: + Collection: "<>" +Do: +- - Id: do1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: do2 + - Id: do2 + StepType: MyApp.DoSomething2, MyApp +``` + + ## While Loops Use the .While method to start a while construct +#### Fluent API + ```C# public class WhileWorkflow : IWorkflow { @@ -49,10 +183,49 @@ public class WhileWorkflow : IWorkflow } ``` +#### JSON / YAML API + +```json +{ + "Id": "MyWhileStep", + "StepType": "WorkflowCore.Primitives.While, WorkflowCore", + "NextStepId": "...", + "Inputs": { "Condition": "<>" }, + "Do": [[ + { + "Id": "do1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "do2" + }, + { + "Id": "do2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ]] +} +``` +```yaml +Id: MyWhileStep +StepType: WorkflowCore.Primitives.While, WorkflowCore +NextStepId: "..." +Inputs: + Condition: "<>" +Do: +- - Id: do1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: do2 + - Id: do2 + StepType: MyApp.DoSomething2, MyApp + +``` + + ## If Conditions Use the .If method to start an if condition +#### Fluent API + ```C# public class IfWorkflow : IWorkflow { @@ -73,10 +246,49 @@ public class IfWorkflow : IWorkflow } ``` +#### JSON / YAML API + +```json +{ + "Id": "MyIfStep", + "StepType": "WorkflowCore.Primitives.If, WorkflowCore", + "NextStepId": "...", + "Inputs": { "Condition": "<>" }, + "Do": [[ + { + "Id": "do1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "do2" + }, + { + "Id": "do2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ]] +} +``` +```yaml +Id: MyIfStep +StepType: WorkflowCore.Primitives.If, WorkflowCore +NextStepId: "..." +Inputs: + Condition: "<>" +Do: +- - Id: do1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: do2 + - Id: do2 + StepType: MyApp.DoSomething2, MyApp + +``` + + ## Parallel Paths Use the .Parallel() method to branch parallel tasks +#### Fluent API + ```C# public class ParallelWorkflow : IWorkflow { @@ -103,10 +315,62 @@ public class ParallelWorkflow : IWorkflow } ``` +#### JSON / YAML API + +```json +{ + "Id": "MyParallelStep", + "StepType": "WorkflowCore.Primitives.Sequence, WorkflowCore", + "NextStepId": "...", + "Do": [ + [ + { + "Id": "Branch1.Step1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "Branch1.Step2" + }, + { + "Id": "Branch1.Step2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ], + [ + { + "Id": "Branch2.Step1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "Branch2.Step2" + }, + { + "Id": "Branch2.Step2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ] + ] +} +``` +```yaml +Id: MyParallelStep +StepType: WorkflowCore.Primitives.Sequence, WorkflowCore +NextStepId: "..." +Do: +- - Id: Branch1.Step1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: Branch1.Step2 + - Id: Branch1.Step2 + StepType: MyApp.DoSomething2, MyApp +- - Id: Branch2.Step1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: Branch2.Step2 + - Id: Branch2.Step2 + StepType: MyApp.DoSomething2, MyApp +``` + + ## Schedule Use `.Schedule` to register a future set of steps to run asynchronously in the background within your workflow. +#### Fluent API ```c# builder @@ -117,11 +381,68 @@ builder .Then(context => Console.WriteLine("Doing normal tasks")); ``` +#### JSON / YAML API + +```json +{ + "Id": "MyScheduleStep", + "StepType": "WorkflowCore.Primitives.Schedule, WorkflowCore", + "Inputs": { "Interval": "<>" }, + "Do": [[ + { + "Id": "do1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "do2" + }, + { + "Id": "do2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ]] +} +``` +```yaml +Id: MyScheduleStep +StepType: WorkflowCore.Primitives.Schedule, WorkflowCore +Inputs: + Interval: "<>" +Do: +- - Id: do1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: do2 + - Id: do2 + StepType: MyApp.DoSomething2, MyApp +``` + + +### Delay + +The `Delay` step will pause the current branch of your workflow for a specified period. + +#### JSON / YAML API + +```json +{ + "Id": "MyDelayStep", + "StepType": "WorkflowCore.Primitives.Delay, WorkflowCore", + "NextStepId": "...", + "Inputs": { "Period": "<>" } +} +``` +```yaml +Id: MyDelayStep +StepType: WorkflowCore.Primitives.Delay, WorkflowCore +NextStepId: "..." +Inputs: + Period: "<>" +``` + ## Recur Use `.Recur` to setup a set of recurring background steps within your workflow, until a certain condition is met +#### Fluent API ```c# builder @@ -132,32 +453,40 @@ builder .Then(context => Console.WriteLine("Carry on")); ``` -### Decision Branches +#### JSON / YAML API -You can define multiple independent branches within your workflow and select one based on an expression value. - -For the fluent API, we define our branches with the `CreateBranch()` method on the workflow builder. We can then select a branch using the `Branch` method. - -The select expressions will be matched to the branch listed via the `Branch` method, and the matching next step(s) will be scheduled to execute next. - -This workflow will select `branch1` if the value of `data.Value1` is `one`, and `branch2` if it is `two`. -```c# -var branch1 = builder.CreateBranch() - .StartWith() - .Input(step => step.Message, data => "hi from 1") - .Then() - .Input(step => step.Message, data => "bye from 1"); - -var branch2 = builder.CreateBranch() - .StartWith() - .Input(step => step.Message, data => "hi from 2") - .Then() - .Input(step => step.Message, data => "bye from 2"); - - -builder - .StartWith() - .Decide(data => data.Value1) - .Branch((data, outcome) => data.Value1 == "one", branch1) - .Branch((data, outcome) => data.Value1 == "two", branch2); +```json +{ + "Id": "MyScheduleStep", + "StepType": "WorkflowCore.Primitives.Recur, WorkflowCore", + "Inputs": { + "Interval": "<>", + "StopCondition": "<>" + }, + "Do": [[ + { + "Id": "do1", + "StepType": "MyApp.DoSomething1, MyApp", + "NextStepId": "do2" + }, + { + "Id": "do2", + "StepType": "MyApp.DoSomething2, MyApp" + } + ]] +} +``` +```yaml +Id: MyScheduleStep +StepType: WorkflowCore.Primitives.Recur, WorkflowCore +Inputs: + Interval: "<>" + StopCondition: "<>" +Do: +- - Id: do1 + StepType: MyApp.DoSomething1, MyApp + NextStepId: do2 + - Id: do2 + StepType: MyApp.DoSomething2, MyApp ``` + diff --git a/docs/error-handling.md b/docs/error-handling.md index 1e7554f7a..61d6f2f5b 100644 --- a/docs/error-handling.md +++ b/docs/error-handling.md @@ -2,6 +2,8 @@ Each step can be configured with it's own error handling behavior, it can be retried at a later time, suspend the workflow or terminate the workflow. +### Fluent API + ```C# public void Build(IWorkflowBuilder builder) { @@ -12,4 +14,25 @@ public void Build(IWorkflowBuilder builder) } ``` -The WorkflowHost service also has a .OnStepError event which can be used to intercept exceptions from workflow steps on a more global level. \ No newline at end of file +### JSON / YAML API + +ErrorBehavior + +```json +{ + "Id": "...", + "StepType": "...", + "ErrorBehavior": "Retry / Suspend / Terminate / Compensate", + "RetryInterval": "00:10:00" +} +``` +```yaml +Id: "..." +StepType: "..." +ErrorBehavior: Retry / Suspend / Terminate / Compensate +RetryInterval: '00:10:00' +``` + +## Global Error handling + +The WorkflowHost service also has a `.OnStepError` event which can be used to intercept exceptions from workflow steps on a more global level. \ No newline at end of file diff --git a/docs/external-events.md b/docs/external-events.md index 9a4214527..6fd8d5108 100644 --- a/docs/external-events.md +++ b/docs/external-events.md @@ -25,3 +25,40 @@ host.PublishEvent("MyEvent", "0", "hello"); You can also specify an effective date when waiting for events, which allows you to respond to events that may have already occurred in the past, or only ones that occur after the effective date. + +## JSON / YAML API + +The `.WaitFor` can be implemented using inputs as follows + +| Field | Description | +| ---------------------- | --------------------------- | +| CancelCondition | Optional expression to specify a cancel condition | +| Inputs.EventName | Expression to specify the event name | +| Inputs.EventKey | Expression to specify the event key | +| Inputs.EffectiveDate | Optional expression to specify the effective date | + + +```json +{ + "Id": "MyWaitStep", + "StepType": "WorkflowCore.Primitives.WaitFor, WorkflowCore", + "NextStepId": "...", + "CancelCondition": "...", + "Inputs": { + "EventName": "\"Event1\"", + "EventKey": "\"Key1\"", + "EffectiveDate": "DateTime.Now" + } +} +``` +```yaml +Id: MyWaitStep +StepType: WorkflowCore.Primitives.WaitFor, WorkflowCore +NextStepId: "..." +CancelCondition: "..." +Inputs: + EventName: '"Event1"' + EventKey: '"Key1"' + EffectiveDate: DateTime.Now + +``` diff --git a/docs/json-yaml.md b/docs/json-yaml.md index acbf0ef2d..cfddd1306 100644 --- a/docs/json-yaml.md +++ b/docs/json-yaml.md @@ -147,398 +147,43 @@ Steps: ``` -### WaitFor - -The `.WaitFor` can be implemented using inputs as follows - -| Field | Description | -| ---------------------- | --------------------------- | -| CancelCondition | Optional expression to specify a cancel condition | -| Inputs.EventName | Expression to specify the event name | -| Inputs.EventKey | Expression to specify the event key | -| Inputs.EffectiveDate | Optional expression to specify the effective date | - - -```json -{ - "Id": "MyWaitStep", - "StepType": "WorkflowCore.Primitives.WaitFor, WorkflowCore", - "NextStepId": "...", - "CancelCondition": "...", - "Inputs": { - "EventName": "\"Event1\"", - "EventKey": "\"Key1\"", - "EffectiveDate": "DateTime.Now" - } -} -``` -```yaml -Id: MyWaitStep -StepType: WorkflowCore.Primitives.WaitFor, WorkflowCore -NextStepId: "..." -CancelCondition: "..." -Inputs: - EventName: '"Event1"' - EventKey: '"Key1"' - EffectiveDate: DateTime.Now - -``` - -### Activity - -The `.Activity` can be implemented using inputs as follows - -| Field | Description | -| ---------------------- | --------------------------- | -| CancelCondition | Optional expression to specify a cancel condition | -| Inputs.ActivityName | Expression to specify the activity name | -| Inputs.Parameters | Expression to specify the parameters to pass the activity worker | -| Inputs.EffectiveDate | Optional expression to specify the effective date | - - -```json -{ - "Id": "MyActivityStep", - "StepType": "WorkflowCore.Primitives.Activity, WorkflowCore", - "NextStepId": "...", - "CancelCondition": "...", - "Inputs": { - "ActivityName": "\"my-activity\"", - "Parameters": "data.SomeValue" - } -} -``` -```yaml -Id: MyActivityStep -StepType: WorkflowCore.Primitives.Activity, WorkflowCore -NextStepId: "..." -CancelCondition: "..." -Inputs: - ActivityName: '"my-activity"' - EventKey: '"Key1"' - Parameters: data.SomeValue - -``` - - -### If - -The `.If` can be implemented as follows - +You can also pass object graphs to step inputs as opposed to just scalar values ```json -{ - "Id": "MyIfStep", - "StepType": "WorkflowCore.Primitives.If, WorkflowCore", - "NextStepId": "...", - "Inputs": { "Condition": "<>" }, - "Do": [[ - { - "Id": "do1", - "StepType": "MyApp.DoSomething1, MyApp", - "NextStepId": "do2" - }, - { - "Id": "do2", - "StepType": "MyApp.DoSomething2, MyApp" - } - ]] -} -``` -```yaml -Id: MyIfStep -StepType: WorkflowCore.Primitives.If, WorkflowCore -NextStepId: "..." -Inputs: - Condition: "<>" -Do: -- - Id: do1 - StepType: MyApp.DoSomething1, MyApp - NextStepId: do2 - - Id: do2 - StepType: MyApp.DoSomething2, MyApp - -``` - -### While - -The `.While` can be implemented as follows - -```json -{ - "Id": "MyWhileStep", - "StepType": "WorkflowCore.Primitives.While, WorkflowCore", - "NextStepId": "...", - "Inputs": { "Condition": "<>" }, - "Do": [[ - { - "Id": "do1", - "StepType": "MyApp.DoSomething1, MyApp", - "NextStepId": "do2" - }, - { - "Id": "do2", - "StepType": "MyApp.DoSomething2, MyApp" - } - ]] -} -``` -```yaml -Id: MyWhileStep -StepType: WorkflowCore.Primitives.While, WorkflowCore -NextStepId: "..." -Inputs: - Condition: "<>" -Do: -- - Id: do1 - StepType: MyApp.DoSomething1, MyApp - NextStepId: do2 - - Id: do2 - StepType: MyApp.DoSomething2, MyApp - -``` - -### ForEach - -The `.ForEach` can be implemented as follows - -```json -{ - "Id": "MyForEachStep", - "StepType": "WorkflowCore.Primitives.ForEach, WorkflowCore", - "NextStepId": "...", - "Inputs": { "Collection": "<>" }, - "Do": [[ - { - "Id": "do1", - "StepType": "MyApp.DoSomething1, MyApp", - "NextStepId": "do2" - }, - { - "Id": "do2", - "StepType": "MyApp.DoSomething2, MyApp" - } - ]] -} -``` -```yaml -Id: MyForEachStep -StepType: WorkflowCore.Primitives.ForEach, WorkflowCore -NextStepId: "..." -Inputs: - Collection: "<>" -Do: -- - Id: do1 - StepType: MyApp.DoSomething1, MyApp - NextStepId: do2 - - Id: do2 - StepType: MyApp.DoSomething2, MyApp -``` - -### Delay - -The `.Delay` can be implemented as follows - -```json -{ - "Id": "MyDelayStep", - "StepType": "WorkflowCore.Primitives.Delay, WorkflowCore", - "NextStepId": "...", - "Inputs": { "Period": "<>" } -} -``` -```yaml -Id: MyDelayStep -StepType: WorkflowCore.Primitives.Delay, WorkflowCore -NextStepId: "..." -Inputs: - Period: "<>" +"inputs": +{ + "Body": { + "Value1": 1, + "Value2": 2 + }, + "Headers": { + "Content-Type": "application/json" + } +}, ``` - -### Parallel - -The `.Parallel` can be implemented as follows - +If you want to evaluate an expression for a given property of your object, simply prepend and `@` and pass an expression string ```json -{ - "Id": "MyParallelStep", - "StepType": "WorkflowCore.Primitives.Sequence, WorkflowCore", - "NextStepId": "...", - "Do": [ - [ - { - "Id": "Branch1.Step1", - "StepType": "MyApp.DoSomething1, MyApp", - "NextStepId": "Branch1.Step2" - }, - { - "Id": "Branch1.Step2", - "StepType": "MyApp.DoSomething2, MyApp" - } - ], - [ - { - "Id": "Branch2.Step1", - "StepType": "MyApp.DoSomething1, MyApp", - "NextStepId": "Branch2.Step2" - }, - { - "Id": "Branch2.Step2", - "StepType": "MyApp.DoSomething2, MyApp" - } - ] - ] -} -``` -```yaml -Id: MyParallelStep -StepType: WorkflowCore.Primitives.Sequence, WorkflowCore -NextStepId: "..." -Do: -- - Id: Branch1.Step1 - StepType: MyApp.DoSomething1, MyApp - NextStepId: Branch1.Step2 - - Id: Branch1.Step2 - StepType: MyApp.DoSomething2, MyApp -- - Id: Branch2.Step1 - StepType: MyApp.DoSomething1, MyApp - NextStepId: Branch2.Step2 - - Id: Branch2.Step2 - StepType: MyApp.DoSomething2, MyApp +"inputs": +{ + "Body": { + "@Value1": "data.MyValue * 2", + "Value2": 5 + }, + "Headers": { + "Content-Type": "application/json" + } +}, ``` -### Schedule +#### Enums -The `.Schedule` can be implemented as follows +If your step has an enum property, you can just pass the string representation of the enum value and it will be automatically converted. -```json -{ - "Id": "MyScheduleStep", - "StepType": "WorkflowCore.Primitives.Schedule, WorkflowCore", - "Inputs": { "Interval": "<>" }, - "Do": [[ - { - "Id": "do1", - "StepType": "MyApp.DoSomething1, MyApp", - "NextStepId": "do2" - }, - { - "Id": "do2", - "StepType": "MyApp.DoSomething2, MyApp" - } - ]] -} -``` -```yaml -Id: MyScheduleStep -StepType: WorkflowCore.Primitives.Schedule, WorkflowCore -Inputs: - Interval: "<>" -Do: -- - Id: do1 - StepType: MyApp.DoSomething1, MyApp - NextStepId: do2 - - Id: do2 - StepType: MyApp.DoSomething2, MyApp -``` +#### Environment variables available in input expressions -### Recur - -The `.Recur` can be implemented as follows - -```json -{ - "Id": "MyScheduleStep", - "StepType": "WorkflowCore.Primitives.Recur, WorkflowCore", - "Inputs": { - "Interval": "<>", - "StopCondition": "<>" - }, - "Do": [[ - { - "Id": "do1", - "StepType": "MyApp.DoSomething1, MyApp", - "NextStepId": "do2" - }, - { - "Id": "do2", - "StepType": "MyApp.DoSomething2, MyApp" - } - ]] -} +You can access environment variables from within input expressions. +usage: ``` -```yaml -Id: MyScheduleStep -StepType: WorkflowCore.Primitives.Recur, WorkflowCore -Inputs: - Interval: "<>" - StopCondition: "<>" -Do: -- - Id: do1 - StepType: MyApp.DoSomething1, MyApp - NextStepId: do2 - - Id: do2 - StepType: MyApp.DoSomething2, MyApp -``` - -### Decision Branches - -You can define multiple independent branches within your workflow and select one based on an expression value. -Hook up your branches via the `SelectNextStep` property, instead of a `NextStepId`. The expressions will be matched to the step Ids listed in `SelectNextStep`, and the matching next step(s) will be scheduled to execute next. - -```json -{ - "Id": "DecisionWorkflow", - "Version": 1, - "DataType": "MyApp.MyData, MyApp", - "Steps": [ - { - "Id": "decide", - "StepType": "...", - "SelectNextStep": - { - "Branch1": "<>", - "Branch2": "<>" - } - }, - { - "Id": "Branch1", - "StepType": "MyApp.PrintMessage, MyApp", - "Inputs": - { - "Message": "\"Hello from 1\"" - } - }, - { - "Id": "Branch2", - "StepType": "MyApp.PrintMessage, MyApp", - "Inputs": - { - "Message": "\"Hello from 2\"" - } - } - ] -} +environment["VARIABLE_NAME"] ``` -```yaml -Id: DecisionWorkflow -Version: 1 -DataType: MyApp.MyData, MyApp -Steps: -- Id: decide - StepType: WorkflowCore.Primitives.Decide, WorkflowCore - Inputs: - Expression: <> - OutcomeSteps: - Branch1: '<>' - Branch2: '<>' -- Id: Branch1 - StepType: MyApp.PrintMessage, MyApp - Inputs: - Message: '"Hello from 1"' -- Id: Branch2 - StepType: MyApp.PrintMessage, MyApp - Inputs: - Message: '"Hello from 2"' -``` \ No newline at end of file From d75fc2c63c7248c7b63470e1955b13e494aed169 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 18 Jan 2020 08:14:41 -0800 Subject: [PATCH 191/462] graceful shutdown --- .../Services/BackgroundTasks/QueueConsumer.cs | 4 +++- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- test/ScratchPad/Program.cs | 13 ++++++++++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 87bc068dc..47097bcc8 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -119,7 +120,8 @@ private async void Execute() } } - await Task.WhenAll(activeTasks.Values); + foreach (var task in activeTasks.Values) + task.Wait(); } private async Task ExecuteItem(string itemId) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index cdb0e0628..a6f4d3a95 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.1.2 - 3.1.2.0 - 3.1.2.0 + 3.1.3 + 3.1.3.0 + 3.1.3.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.1.2 + 3.1.3 diff --git a/test/ScratchPad/Program.cs b/test/ScratchPad/Program.cs index 489a4c086..40c569a29 100644 --- a/test/ScratchPad/Program.cs +++ b/test/ScratchPad/Program.cs @@ -22,12 +22,12 @@ public static void Main(string[] args) var host = serviceProvider.GetService(); var loader = serviceProvider.GetService(); var activityController = serviceProvider.GetService(); - //host.RegisterWorkflow(); - loader.LoadDefinition(Properties.Resources.HelloWorld, Deserializers.Json); + host.RegisterWorkflow(); + //loader.LoadDefinition(Properties.Resources.HelloWorld, Deserializers.Json); host.Start(); - host.StartWorkflow("Test02", 1, new WfData() + host.StartWorkflow("Test01", 1, new WfData() { Value1 = "two", Value2 = "data2" @@ -111,6 +111,13 @@ public void Build(IWorkflowBuilder builder) builder .StartWith() + .Then((context) => + { + Console.WriteLine("------1"); + Task.Delay(TimeSpan.FromSeconds(20)).Wait(); + Console.WriteLine("------2"); + return ExecutionResult.Next(); + }) .Decide(data => data.Value1) .Branch((data, outcome) => data.Value1 == "one", branch1) .Branch((data, outcome) => data.Value1 == "two", branch2); From 0fb0440fb459f9306aaa9cb9d46a5c8cd379859c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 20 Jan 2020 19:33:31 -0800 Subject: [PATCH 192/462] dependency versions (#492) --- src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 2 +- src/WorkflowCore/WorkflowCore.csproj | 16 ++++++++-------- .../WorkflowCore.Providers.AWS.csproj | 8 ++++---- .../WorkflowCore.Providers.Elasticsearch.csproj | 6 +++--- .../WorkflowCore.Providers.Redis.csproj | 8 ++++---- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index 98c8bedae..44808cf0d 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 3.1.2 + 3.1.4 DSL extenstion for Workflow Core provding support for JSON and YAML workflow definitions. Daniel Gerlag diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index a6f4d3a95..ca9c501b6 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,19 +15,19 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.1.3 - 3.1.3.0 - 3.1.3.0 + 3.1.4 + 3.1.4.0 + 3.1.4.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.1.3 + 3.1.4 - - - - + + + + diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 3e7ac61c7..d8470de03 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,16 +11,16 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 3.0.0 - 3.0.0.0 - 3.0.0 + 3.0.1 + 3.0.1.0 + 3.0.1 - + diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index a0374a50c..a980a0d6d 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -1,8 +1,8 @@ - + netstandard2.0 - 2.2.0 + 2.2.1 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git @@ -13,7 +13,7 @@ - + diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index 350e61e80..4513eb506 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -1,19 +1,19 @@ - + netstandard2.0 - 3.0.0 + 3.0.1 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git https://github.com/danielgerlag/workflow-core Redis providers for Workflow Core (Persistence, queueing, distributed locking and event hubs) - 3.0.0 + 3.0.1 - + From 5ec38b22d409fe7955fe8d5fbf39b7e667ba472b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 20 Jan 2020 20:40:56 -0800 Subject: [PATCH 193/462] foreach collection conversion --- src/WorkflowCore.DSL/Services/DefinitionLoader.cs | 7 ++++++- src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index 101b2f5b2..810d6338b 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -268,7 +268,12 @@ void acn(IStepBody pStep, object pData, IStepExecutionContext pContext) if (stepProperty.PropertyType.IsEnum) stepProperty.SetValue(pStep, Enum.Parse(stepProperty.PropertyType, (string)resolvedValue, true)); else - stepProperty.SetValue(pStep, System.Convert.ChangeType(resolvedValue, stepProperty.PropertyType)); + { + if ((resolvedValue != null) && (stepProperty.PropertyType.IsAssignableFrom(resolvedValue.GetType()))) + stepProperty.SetValue(pStep, resolvedValue); + else + stepProperty.SetValue(pStep, System.Convert.ChangeType(resolvedValue, stepProperty.PropertyType)); + } } return acn; } diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index 44808cf0d..913c643f4 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 3.1.4 + 3.1.5 DSL extenstion for Workflow Core provding support for JSON and YAML workflow definitions. Daniel Gerlag From f3356e7cc138f7976675bc84f12b88250294f704 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 21 Jan 2020 06:41:04 -0800 Subject: [PATCH 194/462] Update WorkflowCore.Testing.csproj --- test/WorkflowCore.Testing/WorkflowCore.Testing.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index c0412a10a..32f9c1363 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 2.2.0 - 2.2.0.0 - 2.2.0.0 + 2.3.0 + 2.3.0.0 + 2.3.0.0 Facilitates testing of workflows built on Workflow-Core From d31207bafe38da1efdc03ced1cf6ceb1c5e0b9dd Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 1 Feb 2020 10:42:54 -0800 Subject: [PATCH 195/462] sql context init action (#502) --- .../ServiceCollectionExtensions.cs | 7 ++++--- .../SqlContextFactory.cs | 14 ++++++++++---- .../SqlServerContext.cs | 2 +- .../WorkflowCore.Persistence.SqlServer.csproj | 8 ++++---- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs index 46910a63d..009f1edb7 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Data.Common; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Persistence.EntityFramework.Interfaces; @@ -9,10 +10,10 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { - public static WorkflowOptions UseSqlServer(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB) + public static WorkflowOptions UseSqlServer(this WorkflowOptions options, string connectionString, bool canCreateDB, bool canMigrateDB, Action initAction = null) { - options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new SqlContextFactory(connectionString), canCreateDB, canMigrateDB)); - options.Services.AddTransient(sp => new WorkflowPurger(new SqlContextFactory(connectionString))); + options.UsePersistence(sp => new EntityFrameworkPersistenceProvider(new SqlContextFactory(connectionString, initAction), canCreateDB, canMigrateDB)); + options.Services.AddTransient(sp => new WorkflowPurger(new SqlContextFactory(connectionString, initAction))); return options; } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs index 28e30c4a3..8cb6d57a8 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Data.Common; +using Microsoft.EntityFrameworkCore; using WorkflowCore.Persistence.EntityFramework.Interfaces; using WorkflowCore.Persistence.EntityFramework.Services; @@ -9,15 +10,20 @@ namespace WorkflowCore.Persistence.SqlServer public class SqlContextFactory : IWorkflowDbContextFactory { private readonly string _connectionString; + private readonly Action _initAction; - public SqlContextFactory(string connectionString) + public SqlContextFactory(string connectionString, Action initAction = null) { _connectionString = connectionString; + _initAction = initAction; } - + public WorkflowDbContext Build() { - return new SqlServerContext(_connectionString); + var result = new SqlServerContext(_connectionString); + _initAction?.Invoke(result.Database.GetDbConnection()); + + return result; } } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs index 217ed9629..cce0cad59 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs @@ -21,7 +21,7 @@ public SqlServerContext(string connectionString) _connectionString = connectionString; } - + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 148bba34e..dffda9f93 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,11 +15,11 @@ false false false - 3.0.0 + 3.0.1 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 3.0.0.0 - 3.0.0.0 - 3.0.0 + 3.0.1.0 + 3.0.1.0 + 3.0.1 From 90fe34886e8c2a3f2dd0492b53d8f0c574a2ca91 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 9 Feb 2020 08:08:51 -0800 Subject: [PATCH 196/462] Method to remove definition (#504) * remove definition * isregistered method --- src/WorkflowCore/Interface/IWorkflowRegistry.cs | 2 ++ src/WorkflowCore/Services/WorkflowRegistry.cs | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/WorkflowCore/Interface/IWorkflowRegistry.cs b/src/WorkflowCore/Interface/IWorkflowRegistry.cs index 555b4e84f..6f798b8e7 100644 --- a/src/WorkflowCore/Interface/IWorkflowRegistry.cs +++ b/src/WorkflowCore/Interface/IWorkflowRegistry.cs @@ -8,5 +8,7 @@ public interface IWorkflowRegistry void RegisterWorkflow(WorkflowDefinition definition); void RegisterWorkflow(IWorkflow workflow) where TData : new(); WorkflowDefinition GetDefinition(string workflowId, int? version = null); + bool IsRegistered(string workflowId, int version); + void DeregisterWorkflow(string workflowId, int version); } } diff --git a/src/WorkflowCore/Services/WorkflowRegistry.cs b/src/WorkflowCore/Services/WorkflowRegistry.cs index 024983353..8ed6a6f7b 100644 --- a/src/WorkflowCore/Services/WorkflowRegistry.cs +++ b/src/WorkflowCore/Services/WorkflowRegistry.cs @@ -33,6 +33,15 @@ public WorkflowDefinition GetDefinition(string workflowId, int? version = null) } } + public void DeregisterWorkflow(string workflowId, int version) + { + var definition = _registry.Find(x => x.Item1 == workflowId && x.Item2 == version); + if (definition != null) + { + _registry.Remove(definition); + } + } + public void RegisterWorkflow(IWorkflow workflow) { if (_registry.Any(x => x.Item1 == workflow.Id && x.Item2 == workflow.Version)) @@ -69,5 +78,11 @@ public void RegisterWorkflow(IWorkflow workflow) var def = builder.Build(workflow.Id, workflow.Version); _registry.Add(Tuple.Create(workflow.Id, workflow.Version, def)); } + + public bool IsRegistered(string workflowId, int version) + { + var definition = _registry.Find(x => x.Item1 == workflowId && x.Item2 == version); + return (definition != null); + } } } From 2b37c7f86133febcf66399ecd5f91d3f39cdeafe Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 9 Feb 2020 13:07:04 -0800 Subject: [PATCH 197/462] Update WorkflowCore.csproj --- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index ca9c501b6..27960af9e 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.1.4 - 3.1.4.0 - 3.1.4.0 + 3.1.5 + 3.1.5.0 + 3.1.5.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.1.4 + 3.1.5 From 5bf34b1a69d22056f5563e5884165f8a3106be32 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 23 Feb 2020 07:51:13 -0800 Subject: [PATCH 198/462] Update EF provider libraries (#520) --- WorkflowCore.sln | 7 - ...lowCore.Persistence.EntityFramework.csproj | 12 +- .../MigrationContextFactory.cs | 13 + .../20200223041701_Activities.Designer.cs | 316 ++++++++++++++++++ .../Migrations/20200223041701_Activities.cs | 62 ++++ .../MysqlPersistenceProviderModelSnapshot.cs | 160 ++++++--- .../WorkflowCore.Persistence.MySQL.csproj | 10 +- ...WorkflowCore.Persistence.PostgreSQL.csproj | 16 +- .../SqlServerContext.cs | 3 - .../WorkflowCore.Persistence.SqlServer.csproj | 14 +- .../WorkflowCore.Persistence.Sqlite.csproj | 10 +- .../WorkflowCore.Sample01.csproj | 12 +- .../WorkflowCore.Sample02.csproj | 4 +- .../WorkflowCore.Sample03.csproj | 6 +- .../WorkflowCore.Sample04.csproj | 7 +- .../WorkflowCore.Sample07.csproj | 3 +- .../WorkflowCore.Sample17.csproj | 2 +- test/WorkflowCore.Tests.MySQL/DockerSetup.cs | 3 +- .../WorkflowCore.Tests.PostgreSQL.csproj | 2 +- 19 files changed, 559 insertions(+), 103 deletions(-) create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/MigrationContextFactory.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20200223041701_Activities.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20200223041701_Activities.cs diff --git a/WorkflowCore.sln b/WorkflowCore.sln index d485c9db8..047ccf0c3 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -141,8 +141,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.DSL", "src\Wor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample18", "src\samples\WorkflowCore.Sample18\WorkflowCore.Sample18.csproj", "{5BE6D628-B9DB-4C76-AAEB-8F3800509A84}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchPad\ScratchPad.csproj", "{FD7B9F06-9970-4C30-A2C0-FD7412FF620B}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -345,10 +343,6 @@ Global {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Debug|Any CPU.Build.0 = Debug|Any CPU {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Release|Any CPU.ActiveCfg = Release|Any CPU {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Release|Any CPU.Build.0 = Release|Any CPU - {FD7B9F06-9970-4C30-A2C0-FD7412FF620B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FD7B9F06-9970-4C30-A2C0-FD7412FF620B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FD7B9F06-9970-4C30-A2C0-FD7412FF620B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FD7B9F06-9970-4C30-A2C0-FD7412FF620B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -406,7 +400,6 @@ Global {78217204-B873-40B9-8875-E3925B2FBCEC} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {20B98905-08CB-4854-8E2C-A31A078383E9} = {EF47161E-E399-451C-BDE8-E92AAD3BD761} {5BE6D628-B9DB-4C76-AAEB-8F3800509A84} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} - {FD7B9F06-9970-4C30-A2C0-FD7412FF620B} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index f81abfd03..ed8392a62 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -14,11 +14,11 @@ false false false - 3.0.0 + 3.1.0 Base package for Workflow-core peristence providers using entity framework - 3.0.0.0 - 3.0.0.0 - 3.0.0 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -26,8 +26,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Persistence.MySQL/MigrationContextFactory.cs b/src/providers/WorkflowCore.Persistence.MySQL/MigrationContextFactory.cs new file mode 100644 index 000000000..4a0e8216d --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/MigrationContextFactory.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.EntityFrameworkCore.Design; + +namespace WorkflowCore.Persistence.MySQL +{ + public class MigrationContextFactory : IDesignTimeDbContextFactory + { + public MysqlContext CreateDbContext(string[] args) + { + return new MysqlContext(@"Server=127.0.0.1;Database=myDataBase;Uid=myUsername;Pwd=myPassword;"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20200223041701_Activities.Designer.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20200223041701_Activities.Designer.cs new file mode 100644 index 000000000..7f3acd2ce --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20200223041701_Activities.Designer.cs @@ -0,0 +1,316 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WorkflowCore.Persistence.MySQL; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + [DbContext(typeof(MysqlContext))] + [Migration("20200223041701_Activities")] + partial class Activities + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.2") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("EventData") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("EventId") + .HasColumnType("char(36)"); + + b.Property("EventKey") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("EventName") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("EventTime") + .HasColumnType("datetime(6)"); + + b.Property("IsProcessed") + .HasColumnType("tinyint(1)"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ErrorTime") + .HasColumnType("datetime(6)"); + + b.Property("ExecutionPointerId") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("Message") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("WorkflowId") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Children") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("ContextItem") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("EndTime") + .HasColumnType("datetime(6)"); + + b.Property("EventData") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("EventKey") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("EventName") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("EventPublished") + .HasColumnType("tinyint(1)"); + + b.Property("Id") + .HasColumnType("varchar(50) CHARACTER SET utf8mb4") + .HasMaxLength(50); + + b.Property("Outcome") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("PersistenceData") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("PredecessorId") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Scope") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("SleepUntil") + .HasColumnType("datetime(6)"); + + b.Property("StartTime") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("StepName") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("WorkflowId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AttributeKey") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("AttributeValue") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("ExecutionPointerId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("EventKey") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("EventName") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("ExecutionPointerId") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("ExternalToken") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("ExternalTokenExpiry") + .HasColumnType("datetime(6)"); + + b.Property("ExternalWorkerId") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("SubscribeAsOf") + .HasColumnType("datetime(6)"); + + b.Property("SubscriptionData") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("SubscriptionId") + .HasColumnType("char(200)") + .HasMaxLength(200); + + b.Property("WorkflowId") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CompleteTime") + .HasColumnType("datetime(6)"); + + b.Property("CreateTime") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("Description") + .HasColumnType("varchar(500) CHARACTER SET utf8mb4") + .HasMaxLength(500); + + b.Property("InstanceId") + .HasColumnType("char(200)") + .HasMaxLength(200); + + b.Property("NextExecution") + .HasColumnType("bigint"); + + b.Property("Reference") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Version") + .HasColumnType("int"); + + b.Property("WorkflowDefinitionId") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20200223041701_Activities.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20200223041701_Activities.cs new file mode 100644 index 000000000..38f78d9e5 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20200223041701_Activities.cs @@ -0,0 +1,62 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + public partial class Activities : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ExecutionPointerId", + table: "Subscription", + maxLength: 200, + nullable: true); + + migrationBuilder.AddColumn( + name: "ExternalToken", + table: "Subscription", + maxLength: 200, + nullable: true); + + migrationBuilder.AddColumn( + name: "ExternalTokenExpiry", + table: "Subscription", + nullable: true); + + migrationBuilder.AddColumn( + name: "ExternalWorkerId", + table: "Subscription", + maxLength: 200, + nullable: true); + + migrationBuilder.AddColumn( + name: "SubscriptionData", + table: "Subscription", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ExecutionPointerId", + table: "Subscription"); + + migrationBuilder.DropColumn( + name: "ExternalToken", + table: "Subscription"); + + migrationBuilder.DropColumn( + name: "ExternalTokenExpiry", + table: "Subscription"); + + migrationBuilder.DropColumn( + name: "ExternalWorkerId", + table: "Subscription"); + + migrationBuilder.DropColumn( + name: "SubscriptionData", + table: "Subscription"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs index 2e50c78d3..17a84e5d7 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs @@ -1,8 +1,9 @@ // +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using System; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WorkflowCore.Persistence.MySQL; namespace WorkflowCore.Persistence.MySQL.Migrations { @@ -13,27 +14,34 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("ProductVersion", "3.1.2") .HasAnnotation("Relational:MaxIdentifierLength", 64); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); - b.Property("EventData"); + b.Property("EventData") + .HasColumnType("longtext CHARACTER SET utf8mb4"); - b.Property("EventId"); + b.Property("EventId") + .HasColumnType("char(36)"); b.Property("EventKey") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") .HasMaxLength(200); b.Property("EventName") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") .HasMaxLength(200); - b.Property("EventTime"); + b.Property("EventTime") + .HasColumnType("datetime(6)"); - b.Property("IsProcessed"); + b.Property("IsProcessed") + .HasColumnType("tinyint(1)"); b.HasKey("PersistenceId"); @@ -52,16 +60,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); - b.Property("ErrorTime"); + b.Property("ErrorTime") + .HasColumnType("datetime(6)"); b.Property("ExecutionPointerId") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") .HasMaxLength(100); - b.Property("Message"); + b.Property("Message") + .HasColumnType("longtext CHARACTER SET utf8mb4"); b.Property("WorkflowId") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") .HasMaxLength(100); b.HasKey("PersistenceId"); @@ -72,52 +85,73 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); - b.Property("Active"); + b.Property("Active") + .HasColumnType("tinyint(1)"); - b.Property("Children"); + b.Property("Children") + .HasColumnType("longtext CHARACTER SET utf8mb4"); - b.Property("ContextItem"); + b.Property("ContextItem") + .HasColumnType("longtext CHARACTER SET utf8mb4"); - b.Property("EndTime"); + b.Property("EndTime") + .HasColumnType("datetime(6)"); - b.Property("EventData"); + b.Property("EventData") + .HasColumnType("longtext CHARACTER SET utf8mb4"); b.Property("EventKey") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") .HasMaxLength(100); b.Property("EventName") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") .HasMaxLength(100); - b.Property("EventPublished"); + b.Property("EventPublished") + .HasColumnType("tinyint(1)"); b.Property("Id") + .HasColumnType("varchar(50) CHARACTER SET utf8mb4") .HasMaxLength(50); - b.Property("Outcome"); + b.Property("Outcome") + .HasColumnType("longtext CHARACTER SET utf8mb4"); - b.Property("PersistenceData"); + b.Property("PersistenceData") + .HasColumnType("longtext CHARACTER SET utf8mb4"); b.Property("PredecessorId") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") .HasMaxLength(100); - b.Property("RetryCount"); + b.Property("RetryCount") + .HasColumnType("int"); - b.Property("Scope"); + b.Property("Scope") + .HasColumnType("longtext CHARACTER SET utf8mb4"); - b.Property("SleepUntil"); + b.Property("SleepUntil") + .HasColumnType("datetime(6)"); - b.Property("StartTime"); + b.Property("StartTime") + .HasColumnType("datetime(6)"); - b.Property("Status"); + b.Property("Status") + .HasColumnType("int"); - b.Property("StepId"); + b.Property("StepId") + .HasColumnType("int"); b.Property("StepName") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") .HasMaxLength(100); - b.Property("WorkflowId"); + b.Property("WorkflowId") + .HasColumnType("bigint"); b.HasKey("PersistenceId"); @@ -129,14 +163,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); b.Property("AttributeKey") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") .HasMaxLength(100); - b.Property("AttributeValue"); + b.Property("AttributeValue") + .HasColumnType("longtext CHARACTER SET utf8mb4"); - b.Property("ExecutionPointerId"); + b.Property("ExecutionPointerId") + .HasColumnType("bigint"); b.HasKey("PersistenceId"); @@ -148,22 +186,47 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); b.Property("EventKey") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") .HasMaxLength(200); b.Property("EventName") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("ExecutionPointerId") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") .HasMaxLength(200); - b.Property("StepId"); + b.Property("ExternalToken") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("ExternalTokenExpiry") + .HasColumnType("datetime(6)"); + + b.Property("ExternalWorkerId") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") + .HasMaxLength(200); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("SubscribeAsOf") + .HasColumnType("datetime(6)"); - b.Property("SubscribeAsOf"); + b.Property("SubscriptionData") + .HasColumnType("longtext CHARACTER SET utf8mb4"); b.Property("SubscriptionId") + .HasColumnType("char(200)") .HasMaxLength(200); b.Property("WorkflowId") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") .HasMaxLength(200); b.HasKey("PersistenceId"); @@ -181,30 +244,41 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => { b.Property("PersistenceId") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); - b.Property("CompleteTime"); + b.Property("CompleteTime") + .HasColumnType("datetime(6)"); - b.Property("CreateTime"); + b.Property("CreateTime") + .HasColumnType("datetime(6)"); - b.Property("Data"); + b.Property("Data") + .HasColumnType("longtext CHARACTER SET utf8mb4"); b.Property("Description") + .HasColumnType("varchar(500) CHARACTER SET utf8mb4") .HasMaxLength(500); b.Property("InstanceId") + .HasColumnType("char(200)") .HasMaxLength(200); - b.Property("NextExecution"); + b.Property("NextExecution") + .HasColumnType("bigint"); b.Property("Reference") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") .HasMaxLength(200); - b.Property("Status"); + b.Property("Status") + .HasColumnType("int"); - b.Property("Version"); + b.Property("Version") + .HasColumnType("int"); b.Property("WorkflowDefinitionId") + .HasColumnType("varchar(200) CHARACTER SET utf8mb4") .HasMaxLength(200); b.HasKey("PersistenceId"); @@ -222,7 +296,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") .WithMany("ExecutionPointers") .HasForeignKey("WorkflowId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => @@ -230,7 +305,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") .WithMany("ExtensionAttributes") .HasForeignKey("ExecutionPointerId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); }); #pragma warning restore 612, 618 } diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index 9d5404999..a031e5fba 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -16,17 +16,17 @@ false false Provides support to persist workflows running on Workflow Core to a MySQL database. - 1.2.1 - 1.2.1.0 - 1.2.1.0 + 3.0.1 + 3.0.1.0 + 3.0.1.0 - + all runtime; build; native; contentfiles; analyzers - + diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 8570745e9..7addc52a4 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,10 +15,10 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 3.0.0 - 3.0.0.0 - 3.0.0.0 - 3.0.0 + 3.1.0 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -27,12 +27,12 @@ - - - + + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs index cce0cad59..499c10295 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs @@ -16,9 +16,6 @@ public class SqlServerContext : WorkflowDbContext public SqlServerContext(string connectionString) : base() { - if (!connectionString.Contains("MultipleActiveResultSets")) - connectionString += ";MultipleActiveResultSets=True"; - _connectionString = connectionString; } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index dffda9f93..be52279d1 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,11 +15,11 @@ false false false - 3.0.1 + 3.1.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 3.0.1.0 - 3.0.1.0 - 3.0.1 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -28,11 +28,11 @@ - - + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index ef7de2537..2d2789021 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,10 +16,10 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 3.0.0 - 3.0.0.0 - 3.0.0.0 - 3.0.0 + 3.1.0 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -28,7 +28,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj index 0733913fd..e4a09e491 100644 --- a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj +++ b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj @@ -16,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj index 4e965f8e5..0765d50c3 100644 --- a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj +++ b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj index da418de31..fc4a5d4fd 100644 --- a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj +++ b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index 48964fd7b..82107b0e6 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -12,6 +12,7 @@ + @@ -28,9 +29,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index 76ac7be9e..5ca51a866 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -26,8 +26,7 @@ - - + diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index c28f63d24..26933096d 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -6,7 +6,7 @@ - + diff --git a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs index 04be3a602..f91909f9d 100644 --- a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs @@ -13,8 +13,7 @@ public class MysqlDockerSetup : DockerSetup public static string RootPassword => "rootpwd123"; public override TimeSpan TimeOut => TimeSpan.FromSeconds(60); - - public override string ImageTag => "5.7.24"; + public override string ImageName => "mysql"; public override IList EnvironmentVariables => new List { $"MYSQL_ROOT_PASSWORD={RootPassword}" diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 117e4dc56..1cc7d56bd 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -21,7 +21,7 @@ - + From 934f3e40457ae6a65bc3b3a64e1c497b2028dbe3 Mon Sep 17 00:00:00 2001 From: Julien Perignon Date: Tue, 25 Feb 2020 09:29:22 +1100 Subject: [PATCH 199/462] Upgraded Nest to latest version of the package --- .../Services/ElasticsearchIndexer.cs | 9 +++++---- .../WorkflowCore.Providers.Elasticsearch.csproj | 2 +- test/Docker.Testify/DockerSetup.cs | 2 +- .../ElasticsearchDockerSetup.cs | 6 +++++- .../WorkflowCore.Tests.Elasticsearch.csproj | 4 ++-- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs b/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs index 1c3ee0c83..21e285207 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -78,14 +79,14 @@ public async Task> Search(string terms, int skip, int public async Task Start() { _client = new ElasticClient(_settings); - var nodeInfo = await _client.NodesInfoAsync(); + var nodeInfo = await _client.Nodes.InfoAsync(); if (nodeInfo.Nodes.Values.Any(x => Convert.ToUInt32(x.Version.Split('.')[0]) < 6)) throw new NotSupportedException("Elasticsearch verison 6 or greater is required"); - var exists = await _client.IndexExistsAsync(_indexName); + var exists = await _client.Indices.ExistsAsync(_indexName); if (!exists.Exists) { - await _client.CreateIndexAsync(_indexName); + await _client.Indices.CreateAsync(_indexName); } } @@ -106,7 +107,7 @@ private List, QueryContainer> { Expression> dataExpr = x => x.Data[filter.DataType.FullName]; var fieldExpr = Expression.Convert(filter.Property, typeof(Func)); - field = new Field(Expression.Invoke(fieldExpr, dataExpr)); + field = new Field(Expression.Lambda(Expression.Invoke(fieldExpr, dataExpr), Expression.Parameter(typeof(WorkflowSearchModel)))); } switch (filter) diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index a980a0d6d..bdeebdc40 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/Docker.Testify/DockerSetup.cs b/test/Docker.Testify/DockerSetup.cs index 0bf7055df..2cddecc41 100644 --- a/test/Docker.Testify/DockerSetup.cs +++ b/test/Docker.Testify/DockerSetup.cs @@ -20,7 +20,7 @@ public abstract class DockerSetup : IDisposable public abstract int InternalPort { get; } public virtual string ImageTag => "latest"; - public virtual TimeSpan TimeOut => TimeSpan.FromSeconds(30); + public virtual TimeSpan TimeOut => TimeSpan.FromSeconds(60); public virtual IList EnvironmentVariables => new List(); public int ExternalPort { get; } diff --git a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs index e10dd5aa9..63e334b77 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs +++ b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs @@ -13,9 +13,13 @@ public class ElasticsearchDockerSetup : DockerSetup public static string ConnectionString { get; set; } public override string ImageName => @"elasticsearch"; - public override string ImageTag => "6.5.4"; + public override string ImageTag => "7.5.1"; public override int InternalPort => 9200; + public override IList EnvironmentVariables => new List { + $"discovery.type=single-node" + }; + public override void PublishConnectionInfo() { ConnectionString = $"http://localhost:{ExternalPort}"; diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index f38e2aae5..168be514f 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.2 @@ -8,7 +8,7 @@ - + From 2038f1eda2c891ce4b9e30d4151e7f65d328f17e Mon Sep 17 00:00:00 2001 From: Julien Perignon Date: Thu, 27 Feb 2020 18:48:48 +1100 Subject: [PATCH 200/462] Set the timeout in ElasticSearchDocker Setup instead of the docker default setup --- test/Docker.Testify/DockerSetup.cs | 2 +- .../ElasticsearchDockerSetup.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/Docker.Testify/DockerSetup.cs b/test/Docker.Testify/DockerSetup.cs index 2cddecc41..0bf7055df 100644 --- a/test/Docker.Testify/DockerSetup.cs +++ b/test/Docker.Testify/DockerSetup.cs @@ -20,7 +20,7 @@ public abstract class DockerSetup : IDisposable public abstract int InternalPort { get; } public virtual string ImageTag => "latest"; - public virtual TimeSpan TimeOut => TimeSpan.FromSeconds(60); + public virtual TimeSpan TimeOut => TimeSpan.FromSeconds(30); public virtual IList EnvironmentVariables => new List(); public int ExternalPort { get; } diff --git a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs index 63e334b77..e79779466 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs +++ b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs @@ -15,7 +15,8 @@ public class ElasticsearchDockerSetup : DockerSetup public override string ImageName => @"elasticsearch"; public override string ImageTag => "7.5.1"; public override int InternalPort => 9200; - + public override TimeSpan TimeOut => TimeSpan.FromSeconds(30); + public override IList EnvironmentVariables => new List { $"discovery.type=single-node" }; From 92ddcf20e7312a9403a31e6d75315d9a3394990f Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 29 Feb 2020 19:30:51 -0800 Subject: [PATCH 201/462] bump version --- .../WorkflowCore.Providers.Elasticsearch.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index bdeebdc40..e4263abaf 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 2.2.1 + 3.0.0 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git From 0499b3a5e04487d187ea01aa1012f296c6007031 Mon Sep 17 00:00:00 2001 From: "Matthew M. Keeler" Date: Sat, 14 Mar 2020 23:45:38 -0400 Subject: [PATCH 202/462] Add prefix support for AWS Queues Fixes #521 --- src/providers/WorkflowCore.Providers.AWS/README.md | 2 +- .../ServiceCollectionExtensions.cs | 4 ++-- .../Services/SQSQueueProvider.cs | 10 ++++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.AWS/README.md b/src/providers/WorkflowCore.Providers.AWS/README.md index b0717fc36..f9c000662 100644 --- a/src/providers/WorkflowCore.Providers.AWS/README.md +++ b/src/providers/WorkflowCore.Providers.AWS/README.md @@ -27,7 +27,7 @@ services.AddWorkflow(cfg => { cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "table-prefix"); cfg.UseAwsDynamoLocking(new EnvironmentVariablesAWSCredentials(), new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "workflow-core-locks"); - cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }); + cfg.UseAwsSimpleQueueService(new EnvironmentVariablesAWSCredentials(), new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.USWest2 }, "queues-prefix"); }); ``` diff --git a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs index 6c32f1571..bb43a3397 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs @@ -13,9 +13,9 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { - public static WorkflowOptions UseAwsSimpleQueueService(this WorkflowOptions options, AWSCredentials credentials, AmazonSQSConfig config) + public static WorkflowOptions UseAwsSimpleQueueService(this WorkflowOptions options, AWSCredentials credentials, AmazonSQSConfig config, string queuesPrefix = "workflowcore") { - options.UseQueueProvider(sp => new SQSQueueProvider(credentials, config, sp.GetService())); + options.UseQueueProvider(sp => new SQSQueueProvider(credentials, config, sp.GetService(), queuesPrefix)); return options; } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs index 6daed010b..dd1c15e14 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs @@ -17,13 +17,15 @@ public class SQSQueueProvider : IQueueProvider private readonly ILogger _logger; private readonly IAmazonSQS _client; private readonly Dictionary _queues = new Dictionary(); + private readonly string _queuesPrefix; public bool IsDequeueBlocking => true; - public SQSQueueProvider(AWSCredentials credentials, AmazonSQSConfig config, ILoggerFactory logFactory) + public SQSQueueProvider(AWSCredentials credentials, AmazonSQSConfig config, ILoggerFactory logFactory, string queuesPrefix) { _logger = logFactory.CreateLogger(); _client = new AmazonSQSClient(credentials, config); + _queuesPrefix = queuesPrefix; } public async Task QueueWork(string id, QueueType queue) @@ -54,9 +56,9 @@ public async Task DequeueWork(QueueType queue, CancellationToken cancell public async Task Start() { - var workflowQueue = await _client.CreateQueueAsync(new CreateQueueRequest("workflowcore-workflows")); - var eventQueue = await _client.CreateQueueAsync(new CreateQueueRequest("workflowcore-events")); - var indexQueue = await _client.CreateQueueAsync(new CreateQueueRequest("workflowcore-index")); + var workflowQueue = await _client.CreateQueueAsync(new CreateQueueRequest($"{_queuesPrefix}-workflows")); + var eventQueue = await _client.CreateQueueAsync(new CreateQueueRequest($"{_queuesPrefix}-events")); + var indexQueue = await _client.CreateQueueAsync(new CreateQueueRequest($"{_queuesPrefix}-index")); _queues[QueueType.Workflow] = workflowQueue.QueueUrl; _queues[QueueType.Event] = eventQueue.QueueUrl; From 12e813370727ca258508ba6bbd3696a2d607afe9 Mon Sep 17 00:00:00 2001 From: Mark Tinsley Date: Wed, 1 Apr 2020 11:23:17 -0500 Subject: [PATCH 203/462] Tiny wording fix --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 48ee3b904..49ac1f38e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -20,7 +20,7 @@ public class HelloWorld : StepBody ``` *The `StepBody` and `StepBodyAsync` class implementations are constructed by the workflow host which first tries to use IServiceProvider for dependency injection, if it can't construct it with this method, it will search for a parameterless constructor* -### Then we define the workflow structure by composing a chain of steps. The is done by implementing the IWorkflow interface +### Then we define the workflow structure by composing a chain of steps. This is done by implementing the IWorkflow interface ```C# public class HelloWorldWorkflow : IWorkflow From da86d9a30cbea4d7853c95fdd2e4a352e55dea4f Mon Sep 17 00:00:00 2001 From: eduardocalixtoviasoft <42845879+eduardocalixtoviasoft@users.noreply.github.com> Date: Wed, 8 Apr 2020 10:01:31 -0300 Subject: [PATCH 204/462] Spelling correction Fix readme file "comeplete" to "complete" --- src/samples/WorkflowCore.Sample03/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/samples/WorkflowCore.Sample03/README.md b/src/samples/WorkflowCore.Sample03/README.md index d75950de6..02e8f730a 100644 --- a/src/samples/WorkflowCore.Sample03/README.md +++ b/src/samples/WorkflowCore.Sample03/README.md @@ -50,7 +50,7 @@ public class PassingDataWorkflow : IWorkflow .Input(step => step.Message, data => "The answer is " + data.Value3.ToString()) .Then(context => { - Console.WriteLine("Workflow comeplete"); + Console.WriteLine("Workflow complete"); return ExecutionResult.Next(); }); } From f87c1bd27aa7f435e85a4379ac23637eb50219a8 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 24 Apr 2020 16:20:51 +0200 Subject: [PATCH 205/462] add test for compensate with seq --- .../Scenarios/CompensationScenario2.cs | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs b/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs new file mode 100644 index 000000000..2b2000304 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using System.Linq; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class CompensationScenario2 : WorkflowTest + { + public class MyDataClass + { + public bool ThrowException { get; set; } + } + + public class Workflow : IWorkflow + { + public static bool Event1Fired = false; + public static bool Event2Fired = false; + public static bool TailEventFired = false; + public static bool Compensation1Fired = false; + public static bool Compensation2Fired = false; + + public string Id => "CompensationWorkflow2"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .Then(context => + { + Event1Fired = true; + if ((context.Workflow.Data as MyDataClass).ThrowException) + throw new Exception(); + Event2Fired = true; + }) + .CompensateWithSequence(seq => seq + .StartWith(context => Compensation1Fired = true) + .Then(context => Compensation2Fired = true) + ) + .Then(context => TailEventFired = true); + } + } + + public CompensationScenario2() + { + Setup(); + Workflow.Event1Fired = false; + Workflow.Event2Fired = false; + Workflow.Compensation1Fired = false; + Workflow.Compensation2Fired = false; + Workflow.TailEventFired = false; + } + + [Fact] + public void NoExceptionScenario() + { + var workflowId = StartWorkflow(new MyDataClass() { ThrowException = false }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + Workflow.Event1Fired.Should().BeTrue(); + Workflow.Event2Fired.Should().BeTrue(); + Workflow.Compensation1Fired.Should().BeFalse(); + Workflow.Compensation2Fired.Should().BeFalse(); + Workflow.TailEventFired.Should().BeTrue(); + } + + [Fact] + public void ExceptionScenario() + { + var workflowId = StartWorkflow(new MyDataClass() { ThrowException = true }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(1); + Workflow.Event1Fired.Should().BeTrue(); + Workflow.Event2Fired.Should().BeFalse(); + Workflow.Compensation1Fired.Should().BeTrue(); + Workflow.Compensation2Fired.Should().BeTrue(); + Workflow.TailEventFired.Should().BeTrue(); + } + } +} From e556030c4f53a058dbd54ae35207f9eaa9d63052 Mon Sep 17 00:00:00 2001 From: Aleksei Petrov Date: Tue, 12 May 2020 10:09:44 +0300 Subject: [PATCH 206/462] Fix PostgreSQL environment variables --- test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs b/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs index 79161fdb5..c5e1d93b1 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs @@ -19,6 +19,10 @@ public class PostgresDockerSetup : DockerSetup public override string ImageName => "postgres"; public override int InternalPort => 5432; + private const string PostgresHostAuthMethod = "trust"; + public override IList EnvironmentVariables => new List { + $"POSTGRES_HOST_AUTH_METHOD={PostgresHostAuthMethod}" + }; public override void PublishConnectionInfo() { ConnectionString = $"Server=127.0.0.1;Port={ExternalPort};Database=workflow;User Id=postgres;"; From de6410d7b97497f57168e7ae897d059408265000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BB=92=E7=AB=B9=20=E5=90=B3?= Date: Tue, 19 May 2020 14:25:35 +0800 Subject: [PATCH 207/462] Add Using WorkFlowCore.DSL in README YAML&JSON Part --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7faad2599..a7dac4994 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ public class MyWorkflow : IWorkflow ## JSON / YAML Workflow Definitions -Define your workflows in JSON or YAML +Define your workflows in JSON or YAML, need to install WorkFlowCore.DSL ```json { @@ -65,7 +65,7 @@ Steps: StepType: MyApp.GoodbyeWorld, MyApp ``` -### Sample use cases +## Sample use cases * New user workflow ```c# From 6ba5040ebccbc8a8bca48beab5b2dc1fdd85865a Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 8 Jun 2020 14:57:35 +0200 Subject: [PATCH 208/462] Allow dependency injection on Workflow --- src/WorkflowCore/Interface/IWorkflowController.cs | 4 ++-- src/WorkflowCore/Services/WorkflowController.cs | 13 ++++++++----- src/WorkflowCore/Services/WorkflowHost.cs | 11 +++++------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/WorkflowCore/Interface/IWorkflowController.cs b/src/WorkflowCore/Interface/IWorkflowController.cs index 757351351..89e7fc3c6 100644 --- a/src/WorkflowCore/Interface/IWorkflowController.cs +++ b/src/WorkflowCore/Interface/IWorkflowController.cs @@ -13,8 +13,8 @@ public interface IWorkflowController Task StartWorkflow(string workflowId, int? version, TData data = null, string reference=null) where TData : class, new(); Task PublishEvent(string eventName, string eventKey, object eventData, DateTime? effectiveDate = null); - void RegisterWorkflow() where TWorkflow : IWorkflow, new(); - void RegisterWorkflow() where TWorkflow : IWorkflow, new() where TData : new(); + void RegisterWorkflow() where TWorkflow : IWorkflow; + void RegisterWorkflow() where TWorkflow : IWorkflow where TData : new(); /// /// Suspend the execution of a given workflow until .ResumeWorkflow is called diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index fc7c7b810..203da3139 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -3,6 +3,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using WorkflowCore.Exceptions; using WorkflowCore.Interface; @@ -19,9 +20,10 @@ public class WorkflowController : IWorkflowController private readonly IQueueProvider _queueProvider; private readonly IExecutionPointerFactory _pointerFactory; private readonly ILifeCycleEventHub _eventHub; + private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; - public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory) + public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory, IServiceProvider serviceProvider) { _persistenceStore = persistenceStore; _lockProvider = lockProvider; @@ -29,6 +31,7 @@ public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLoc _queueProvider = queueProvider; _pointerFactory = pointerFactory; _eventHub = eventHub; + _serviceProvider = serviceProvider; _logger = loggerFactory.CreateLogger(); } @@ -213,17 +216,17 @@ await _eventHub.PublishNotification(new WorkflowTerminated() } public void RegisterWorkflow() - where TWorkflow : IWorkflow, new() + where TWorkflow : IWorkflow { - TWorkflow wf = new TWorkflow(); + TWorkflow wf = ActivatorUtilities.CreateInstance(_serviceProvider); _registry.RegisterWorkflow(wf); } public void RegisterWorkflow() - where TWorkflow : IWorkflow, new() + where TWorkflow : IWorkflow where TData : new() { - TWorkflow wf = new TWorkflow(); + TWorkflow wf = ActivatorUtilities.CreateInstance(_serviceProvider); _registry.RegisterWorkflow(wf); } } diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index 9092e6487..aa08ba727 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -7,6 +7,7 @@ using WorkflowCore.Interface; using WorkflowCore.Models; using System.Reflection; +using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Exceptions; using WorkflowCore.Models.LifeCycleEvents; @@ -121,18 +122,16 @@ public async Task StopAsync(CancellationToken cancellationToken) } public void RegisterWorkflow() - where TWorkflow : IWorkflow, new() + where TWorkflow : IWorkflow { - TWorkflow wf = new TWorkflow(); - Registry.RegisterWorkflow(wf); + _workflowController.RegisterWorkflow(); } public void RegisterWorkflow() - where TWorkflow : IWorkflow, new() + where TWorkflow : IWorkflow where TData : new() { - TWorkflow wf = new TWorkflow(); - Registry.RegisterWorkflow(wf); + _workflowController.RegisterWorkflow(); } public Task SuspendWorkflow(string workflowId) From 3485fd01718a9075265963fbf5e29ef55cba4a86 Mon Sep 17 00:00:00 2001 From: "Matthew M. Keeler" Date: Wed, 17 Jun 2020 00:24:41 -0400 Subject: [PATCH 209/462] Update AWS provider versions --- .../WorkflowCore.Providers.AWS.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index d8470de03..84eb6cb74 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,9 +11,9 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 3.0.1 - 3.0.1.0 - 3.0.1 + 3.0.2 + 3.0.2.0 + 3.0.2 From 8d2ee35927d5c36658f5cd5c8f85278b75d90a5d Mon Sep 17 00:00:00 2001 From: Nikita Kirsanov Date: Thu, 18 Jun 2020 12:55:38 +0700 Subject: [PATCH 210/462] Update mkdocs.yml fix little typo --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 1081d0f05..fcbf9e4a8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,7 +4,7 @@ nav: - Getting started: getting-started.md - External events: external-events.md - Activity workers: activities.md - - Error handing: error-handling.md + - Error handling: error-handling.md - Control structures: control-structures.md - Saga transactions: sagas.md - JSON / YAML Definitions: json-yaml.md @@ -15,4 +15,4 @@ nav: - Test helpers: test-helpers.md - Extensions: extensions.md - Samples: samples.md -theme: readthedocs \ No newline at end of file +theme: readthedocs From 40e97ecd0b64e6807148c6e12976f5052e803dbd Mon Sep 17 00:00:00 2001 From: glucaci Date: Thu, 18 Jun 2020 08:50:06 +0200 Subject: [PATCH 211/462] Increment version --- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 27960af9e..920413a73 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.1.5 - 3.1.5.0 - 3.1.5.0 + 3.1.6 + 3.1.6.0 + 3.1.6.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png 3.1.5 From cb6c290fa803fb51bf71a1514c438b6013475530 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 23 Jun 2020 18:34:33 -0700 Subject: [PATCH 212/462] Update elastic-search.md --- docs/elastic-search.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/elastic-search.md b/docs/elastic-search.md index ce187de80..e921f958d 100644 --- a/docs/elastic-search.md +++ b/docs/elastic-search.md @@ -21,7 +21,7 @@ dotnet add package WorkflowCore.Providers.Elasticsearch Use the `.UseElasticsearch` extension method on `IServiceCollection` when building your service provider -```C# +``` using Nest; ... services.AddWorkflow(cfg => @@ -50,7 +50,7 @@ This will do a full text search on the following default fields In addition you can search data within your own custom data object if it implements `ISearchable` - ```c# + ``` using WorkflowCore.Interfaces; ... public class MyData : ISearchable @@ -72,7 +72,7 @@ This will do a full text search on the following default fields ##### Examples Search all fields for "puppies" - ```c# + ``` searchIndex.Search("puppies", 0, 10); ``` @@ -96,7 +96,7 @@ The following filter types are available ##### Examples Filtering by reference - ```c# + ``` using WorkflowCore.Models.Search; ... @@ -104,22 +104,22 @@ The following filter types are available ``` Filtering by workflows started after a date - ```c# + ``` searchIndex.Search("", 0, 10, DateRangeFilter.After(x => x.CreateTime, startDate)); ``` Filtering by workflows completed within a period - ```c# + ``` searchIndex.Search("", 0, 10, DateRangeFilter.Between(x => x.CompleteTime, startDate, endDate)); ``` Filtering by workflows in a state - ```c# + ``` searchIndex.Search("", 0, 10, StatusFilter.Equals(WorkflowStatus.Complete)); ``` Filtering against your own custom data class - ```c# + ``` class MyData { From 7df09a6fcf82ca307672b14a0eca26669cea2a75 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 3 Jul 2020 16:46:19 -0700 Subject: [PATCH 213/462] Update WorkflowCore.csproj --- src/WorkflowCore/WorkflowCore.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 920413a73..fca9b4de3 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -20,7 +20,7 @@ 3.1.6.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.1.5 + 3.1.6 From dac9b30b12b973632771bb4344879588b6f71f3b Mon Sep 17 00:00:00 2001 From: Leon Segal Date: Sun, 5 Jul 2020 14:44:58 +0200 Subject: [PATCH 214/462] #274 add registry.getAllDefinitions to retrieve all registered workflows --- src/WorkflowCore/Interface/IWorkflowRegistry.cs | 4 +++- src/WorkflowCore/Services/WorkflowRegistry.cs | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/WorkflowCore/Interface/IWorkflowRegistry.cs b/src/WorkflowCore/Interface/IWorkflowRegistry.cs index 6f798b8e7..40143e3c1 100644 --- a/src/WorkflowCore/Interface/IWorkflowRegistry.cs +++ b/src/WorkflowCore/Interface/IWorkflowRegistry.cs @@ -1,4 +1,5 @@ -using WorkflowCore.Models; +using System.Collections.Generic; +using WorkflowCore.Models; namespace WorkflowCore.Interface { @@ -10,5 +11,6 @@ public interface IWorkflowRegistry WorkflowDefinition GetDefinition(string workflowId, int? version = null); bool IsRegistered(string workflowId, int version); void DeregisterWorkflow(string workflowId, int version); + IEnumerable GetAllDefinitions(); } } diff --git a/src/WorkflowCore/Services/WorkflowRegistry.cs b/src/WorkflowCore/Services/WorkflowRegistry.cs index 8ed6a6f7b..5677d434d 100644 --- a/src/WorkflowCore/Services/WorkflowRegistry.cs +++ b/src/WorkflowCore/Services/WorkflowRegistry.cs @@ -84,5 +84,10 @@ public bool IsRegistered(string workflowId, int version) var definition = _registry.Find(x => x.Item1 == workflowId && x.Item2 == version); return (definition != null); } + + public IEnumerable GetAllDefinitions() + { + return _registry.Select(i => i.Item3); + } } } From db293fc08de4c012eb5030f90eaac7fc8687bfa6 Mon Sep 17 00:00:00 2001 From: Leon Segal Date: Fri, 10 Jul 2020 14:09:29 +0200 Subject: [PATCH 215/462] add foreach.RunParallel option to allow synchronous execution of child branches --- README.md | 2 + WorkflowCore.sln | 7 ++ src/WorkflowCore/Interface/IStepBuilder.cs | 7 ++ .../Models/IteratorPersistenceData.cs | 7 ++ src/WorkflowCore/Primitives/Foreach.cs | 31 +++++++-- .../Services/FluentBuilders/StepBuilder.cs | 19 ++++++ .../Services/MongoPersistenceProvider.cs | 1 + src/samples/WorkflowCore.Sample09/README.md | 4 +- .../ForEachSyncWorkflow.cs | 26 ++++++++ src/samples/WorkflowCore.Sample09s/Program.cs | 45 +++++++++++++ src/samples/WorkflowCore.Sample09s/README.md | 29 +++++++++ .../Steps/DisplayContext.cs | 20 ++++++ .../Steps/DoSomething.cs | 17 +++++ .../Steps/SayGoodbye.cs | 17 +++++ .../WorkflowCore.Sample09s/Steps/SayHello.cs | 17 +++++ .../WorkflowCore.Sample09s.csproj | 19 ++++++ .../Scenarios/ForeachSyncScenario.cs | 64 +++++++++++++++++++ 17 files changed, 323 insertions(+), 9 deletions(-) create mode 100644 src/WorkflowCore/Models/IteratorPersistenceData.cs create mode 100644 src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs create mode 100644 src/samples/WorkflowCore.Sample09s/Program.cs create mode 100644 src/samples/WorkflowCore.Sample09s/README.md create mode 100644 src/samples/WorkflowCore.Sample09s/Steps/DisplayContext.cs create mode 100644 src/samples/WorkflowCore.Sample09s/Steps/DoSomething.cs create mode 100644 src/samples/WorkflowCore.Sample09s/Steps/SayGoodbye.cs create mode 100644 src/samples/WorkflowCore.Sample09s/Steps/SayHello.cs create mode 100644 src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/ForeachSyncScenario.cs diff --git a/README.md b/README.md index a7dac4994..9c55cc475 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,8 @@ These are also available as separate Nuget packages. * [Parallel ForEach](src/samples/WorkflowCore.Sample09) +* [Sync ForEach](src/samples/WorkflowCore.Sample09s) + * [While Loop](src/samples/WorkflowCore.Sample10) * [If Statement](src/samples/WorkflowCore.Sample11) diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 047ccf0c3..81030f058 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -141,6 +141,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.DSL", "src\Wor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample18", "src\samples\WorkflowCore.Sample18\WorkflowCore.Sample18.csproj", "{5BE6D628-B9DB-4C76-AAEB-8F3800509A84}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample09s", "src\samples\WorkflowCore.Sample09s\WorkflowCore.Sample09s.csproj", "{E32CF21A-29CC-46D1-8044-FCC327F2B281}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -343,6 +345,10 @@ Global {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Debug|Any CPU.Build.0 = Debug|Any CPU {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Release|Any CPU.ActiveCfg = Release|Any CPU {5BE6D628-B9DB-4C76-AAEB-8F3800509A84}.Release|Any CPU.Build.0 = Release|Any CPU + {E32CF21A-29CC-46D1-8044-FCC327F2B281}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E32CF21A-29CC-46D1-8044-FCC327F2B281}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E32CF21A-29CC-46D1-8044-FCC327F2B281}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E32CF21A-29CC-46D1-8044-FCC327F2B281}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -400,6 +406,7 @@ Global {78217204-B873-40B9-8875-E3925B2FBCEC} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {20B98905-08CB-4854-8E2C-A31A078383E9} = {EF47161E-E399-451C-BDE8-E92AAD3BD761} {5BE6D628-B9DB-4C76-AAEB-8F3800509A84} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} + {E32CF21A-29CC-46D1-8044-FCC327F2B281} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/WorkflowCore/Interface/IStepBuilder.cs b/src/WorkflowCore/Interface/IStepBuilder.cs index db28b5887..d5ebcec7b 100644 --- a/src/WorkflowCore/Interface/IStepBuilder.cs +++ b/src/WorkflowCore/Interface/IStepBuilder.cs @@ -186,6 +186,13 @@ public interface IStepBuilder /// IContainerStepBuilder ForEach(Expression> collection); + /// + /// Execute a block of steps, once for each item in a collection in a RunParallel foreach + /// + /// Resolves a collection for iterate over + /// + IContainerStepBuilder ForEach(Expression> collection, Expression> runParallel); + /// /// Repeat a block of steps until a condition becomes true /// diff --git a/src/WorkflowCore/Models/IteratorPersistenceData.cs b/src/WorkflowCore/Models/IteratorPersistenceData.cs new file mode 100644 index 000000000..1d5ee1b8b --- /dev/null +++ b/src/WorkflowCore/Models/IteratorPersistenceData.cs @@ -0,0 +1,7 @@ +namespace WorkflowCore.Models +{ + public class IteratorPersistenceData : ControlPersistenceData + { + public int Index { get; set; } = 0; + } +} diff --git a/src/WorkflowCore/Primitives/Foreach.cs b/src/WorkflowCore/Primitives/Foreach.cs index 23cecc947..dd4454583 100644 --- a/src/WorkflowCore/Primitives/Foreach.cs +++ b/src/WorkflowCore/Primitives/Foreach.cs @@ -8,28 +8,45 @@ namespace WorkflowCore.Primitives { public class Foreach : ContainerStepBody { - public IEnumerable Collection { get; set; } + public IEnumerable Collection { get; set; } + public bool RunParallel { get; set; } = true; public override ExecutionResult Run(IStepExecutionContext context) { if (context.PersistenceData == null) { var values = Collection.Cast(); - return ExecutionResult.Branch(new List(values), new ControlPersistenceData() { ChildrenActive = true }); + if (RunParallel) + { + return ExecutionResult.Branch(new List(values), new IteratorPersistenceData() { ChildrenActive = true }); + } + else + { + return ExecutionResult.Branch(new List(new object[] { values.ElementAt(0) }), new IteratorPersistenceData() { ChildrenActive = true }); + } } - if (context.PersistenceData is ControlPersistenceData) + if (context.PersistenceData is IteratorPersistenceData persistanceData && persistanceData?.ChildrenActive == true) { - if ((context.PersistenceData as ControlPersistenceData).ChildrenActive) + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) { - if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) + if (!RunParallel) { - return ExecutionResult.Next(); + var values = Collection.Cast(); + persistanceData.Index++; + if (persistanceData.Index < values.Count()) + { + return ExecutionResult.Branch(new List(new object[] { values.ElementAt(persistanceData.Index) }), persistanceData); + } } + + return ExecutionResult.Next(); } + + return ExecutionResult.Persist(persistanceData); } return ExecutionResult.Persist(context.PersistenceData); - } + } } } diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index 93f965eba..e1d4dcc76 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -306,6 +306,25 @@ public IContainerStepBuilder ForEach(Expression ForEach(Expression> collection, Expression> runParallel) + { + var newStep = new WorkflowStep(); + + Expression> inputExpr = (x => x.Collection); + newStep.Inputs.Add(new MemberMapParameter(collection, inputExpr)); + + Expression> pExpr = (x => x.RunParallel); + newStep.Inputs.Add(new MemberMapParameter(runParallel, pExpr)); + + WorkflowBuilder.AddStep(newStep); + var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); + + Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + + return stepBuilder; + } + + public IContainerStepBuilder While(Expression> condition) { var newStep = new WorkflowStep(); diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 8edd69615..40d4e5409 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -79,6 +79,7 @@ static MongoPersistenceProvider() BsonClassMap.RegisterClassMap(x => x.AutoMap()); BsonClassMap.RegisterClassMap(x => x.AutoMap()); + BsonClassMap.RegisterClassMap(x => x.AutoMap()); } static bool indexesCreated = false; diff --git a/src/samples/WorkflowCore.Sample09/README.md b/src/samples/WorkflowCore.Sample09/README.md index 9c7b54bc8..a9b2fbe2a 100644 --- a/src/samples/WorkflowCore.Sample09/README.md +++ b/src/samples/WorkflowCore.Sample09/README.md @@ -1,4 +1,4 @@ -# Foreach sample +# Foreach Parallel sample Illustrates how to implement a parallel foreach within your workflow. @@ -14,7 +14,7 @@ builder .Then(); ``` -or get the collectioin from workflow data. +or get the collection from workflow data. ```c# builder diff --git a/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs b/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs new file mode 100644 index 000000000..685b69ed1 --- /dev/null +++ b/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample09s +{ + public class ForEachSyncWorkflow : IWorkflow + { + public string Id => "ForeachSync"; + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .ForEach(data => new List() { 1, 2, 3, 4 }, data => false) + .Do(x => x + .StartWith() + .Input(step => step.Item, (data, context) => context.Item) + .Then()) + .Then(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample09s/Program.cs b/src/samples/WorkflowCore.Sample09s/Program.cs new file mode 100644 index 000000000..9fd4dcb0f --- /dev/null +++ b/src/samples/WorkflowCore.Sample09s/Program.cs @@ -0,0 +1,45 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using WorkflowCore.Interface; + +namespace WorkflowCore.Sample09s +{ + class Program + { + public static void Main(string[] args) + { + IServiceProvider serviceProvider = ConfigureServices(); + + //start the workflow host + var host = serviceProvider.GetService(); + host.RegisterWorkflow(); + host.Start(); + + Console.WriteLine("Starting workflow..."); + string workflowId = host.StartWorkflow("Foreach").Result; + + + Console.ReadLine(); + host.Stop(); + } + + private static IServiceProvider ConfigureServices() + { + //setup dependency injection + IServiceCollection services = new ServiceCollection(); + services.AddLogging(); + services.AddWorkflow(); + //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow-test002")); + //services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore3;Trusted_Connection=True;", true, true)); + //services.AddWorkflow(x => x.UseSqlite(@"Data Source=database2.db;", true)); + //services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=32768;Database=workflow_test002;User Id=postgres;", true, true)); + + + var serviceProvider = services.BuildServiceProvider(); + + return serviceProvider; + } + + } +} \ No newline at end of file diff --git a/src/samples/WorkflowCore.Sample09s/README.md b/src/samples/WorkflowCore.Sample09s/README.md new file mode 100644 index 000000000..ea6efae02 --- /dev/null +++ b/src/samples/WorkflowCore.Sample09s/README.md @@ -0,0 +1,29 @@ +# Foreach Sync sample + +Illustrates how to implement a synchronous foreach within your workflow. + + +```c# +builder + .StartWith() + .ForEach(data => new List() { 1, 2, 3, 4 }, data => false) + .Do(x => x + .StartWith() + .Input(step => step.Item, (data, context) => context.Item) + .Then()) + .Then(); +``` + +or get the collection from workflow data. + +```c# +builder + .StartWith() + .ForEach(data => data.MyCollection, data => false) + .Do(x => x + .StartWith() + .Input(step => step.Item, (data, context) => context.Item) + .Then()) + .Then(); + +``` diff --git a/src/samples/WorkflowCore.Sample09s/Steps/DisplayContext.cs b/src/samples/WorkflowCore.Sample09s/Steps/DisplayContext.cs new file mode 100644 index 000000000..d7ef216b3 --- /dev/null +++ b/src/samples/WorkflowCore.Sample09s/Steps/DisplayContext.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample09s +{ + public class DisplayContext : StepBody + { + + public object Item { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine($"Working on item {Item}"); + return ExecutionResult.Next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample09s/Steps/DoSomething.cs b/src/samples/WorkflowCore.Sample09s/Steps/DoSomething.cs new file mode 100644 index 000000000..39344b389 --- /dev/null +++ b/src/samples/WorkflowCore.Sample09s/Steps/DoSomething.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample09s +{ + public class DoSomething : StepBody + { + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Doing something..."); + return ExecutionResult.Next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample09s/Steps/SayGoodbye.cs b/src/samples/WorkflowCore.Sample09s/Steps/SayGoodbye.cs new file mode 100644 index 000000000..34c74103b --- /dev/null +++ b/src/samples/WorkflowCore.Sample09s/Steps/SayGoodbye.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample09s +{ + public class SayGoodbye : StepBody + { + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Goodbye"); + return ExecutionResult.Next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample09s/Steps/SayHello.cs b/src/samples/WorkflowCore.Sample09s/Steps/SayHello.cs new file mode 100644 index 000000000..1597910f5 --- /dev/null +++ b/src/samples/WorkflowCore.Sample09s/Steps/SayHello.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample09s +{ + public class SayHello : StepBody + { + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Hello"); + return ExecutionResult.Next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj new file mode 100644 index 000000000..e569ec895 --- /dev/null +++ b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ForeachSyncScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachSyncScenario.cs new file mode 100644 index 000000000..61cf3484c --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachSyncScenario.cs @@ -0,0 +1,64 @@ +using FluentAssertions; +using System; +using System.Collections.Generic; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Primitives; +using WorkflowCore.Testing; +using Xunit; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class ForeachSyncScenario : WorkflowTest + { + public class DoSomething : StepBody + { + public int Counter { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + return ExecutionResult.Next(); + } + } + + public class MyDataClass + { + public int Counter { get; set; } + } + + public class ForeachSyncWorkflow : IWorkflow + { + public string Id => "ForeachSyncWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(_ => ExecutionResult.Next()) + .ForEach(x => new List() { 10, 2, 3 }, _ => false) + .Do(x => x + .StartWith() + .Input(step => step.Period, (_, context) => TimeSpan.FromSeconds((int)context.Item)) + .Then() + .Input(step => step.Counter, (data, context) => (int)context.Item) + .Output(data => data.Counter, step => step.Counter) + ); + } + } + + public ForeachSyncScenario() + { + Setup(); + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(null); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + GetData(workflowId).Counter.Should().Be(3); + UnhandledStepErrors.Count.Should().Be(0); + } + } +} From 4c940a6ea78947466adb8915de18e286a7718382 Mon Sep 17 00:00:00 2001 From: Leon Segal Date: Sat, 11 Jul 2020 21:39:33 +0200 Subject: [PATCH 216/462] add Foreach support for ControlPersistenceData for backward compatibility --- src/WorkflowCore/Primitives/Foreach.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/WorkflowCore/Primitives/Foreach.cs b/src/WorkflowCore/Primitives/Foreach.cs index dd4454583..d72371e56 100644 --- a/src/WorkflowCore/Primitives/Foreach.cs +++ b/src/WorkflowCore/Primitives/Foreach.cs @@ -26,24 +26,32 @@ public override ExecutionResult Run(IStepExecutionContext context) } } - if (context.PersistenceData is IteratorPersistenceData persistanceData && persistanceData?.ChildrenActive == true) + if (context.PersistenceData is IteratorPersistenceData persistenceData && persistenceData?.ChildrenActive == true) { if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) { if (!RunParallel) { var values = Collection.Cast(); - persistanceData.Index++; - if (persistanceData.Index < values.Count()) + persistenceData.Index++; + if (persistenceData.Index < values.Count()) { - return ExecutionResult.Branch(new List(new object[] { values.ElementAt(persistanceData.Index) }), persistanceData); + return ExecutionResult.Branch(new List(new object[] { values.ElementAt(persistenceData.Index) }), persistenceData); } } return ExecutionResult.Next(); } - return ExecutionResult.Persist(persistanceData); + return ExecutionResult.Persist(persistenceData); + } + + if (context.PersistenceData is ControlPersistenceData controlPersistenceData && controlPersistenceData?.ChildrenActive == true) + { + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) + { + return ExecutionResult.Next(); + } } return ExecutionResult.Persist(context.PersistenceData); From 3e70f58918a66e5d36b1c02662a29eccb64eff5a Mon Sep 17 00:00:00 2001 From: Andrey Borisko Date: Thu, 23 Jul 2020 16:19:49 -0400 Subject: [PATCH 217/462] #607 switched to ConcurrentHashSet. --- src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs | 7 ++++--- src/WorkflowCore/WorkflowCore.csproj | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 47097bcc8..54d384879 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using ConcurrentCollections; using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -54,7 +55,7 @@ private async void Execute() { var cancelToken = _cancellationTokenSource.Token; var activeTasks = new Dictionary(); - var secondPasses = new HashSet(); + var secondPasses = new ConcurrentHashSet(); while (!cancelToken.IsCancellationRequested) { @@ -83,7 +84,7 @@ private async void Execute() continue; } - secondPasses.Remove(item); + secondPasses.TryRemove(item); var task = new Task(async (object data) => { @@ -92,7 +93,7 @@ private async void Execute() await ExecuteItem((string)data); while (EnableSecondPasses && secondPasses.Contains(item)) { - secondPasses.Remove(item); + secondPasses.TryRemove(item); await ExecuteItem((string)data); } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index fca9b4de3..b4748923d 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -24,6 +24,7 @@ + From 9af7c6f2794024c61f46cf2101ca4d7d77681d6b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 31 Jul 2020 14:59:57 -0700 Subject: [PATCH 218/462] option to auto delete complete workflows on redis --- .../ServiceCollectionExtensions.cs | 4 ++-- .../Services/RedisPersistenceProvider.cs | 8 +++++++- .../WorkflowCore.Providers.Redis.csproj | 4 ++-- .../RedisPersistenceProviderFixture.cs | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs index a517690f2..ccc96a98c 100644 --- a/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs @@ -20,9 +20,9 @@ public static WorkflowOptions UseRedisLocking(this WorkflowOptions options, stri return options; } - public static WorkflowOptions UseRedisPersistence(this WorkflowOptions options, string connectionString, string prefix) + public static WorkflowOptions UseRedisPersistence(this WorkflowOptions options, string connectionString, string prefix, bool deleteComplete = false) { - options.UsePersistence(sp => new RedisPersistenceProvider(connectionString, prefix, sp.GetService())); + options.UsePersistence(sp => new RedisPersistenceProvider(connectionString, prefix, deleteComplete, sp.GetService())); return options; } diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index 621f01e8b..ad92848e5 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -25,14 +25,16 @@ public class RedisPersistenceProvider : IPersistenceProvider private readonly IDatabase _redis; private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + private readonly bool _removeComplete; - public RedisPersistenceProvider(string connectionString, string prefix, ILoggerFactory logFactory) + public RedisPersistenceProvider(string connectionString, string prefix, bool removeComplete, ILoggerFactory logFactory) { _connectionString = connectionString; _prefix = prefix; _logger = logFactory.CreateLogger(GetType()); _multiplexer = ConnectionMultiplexer.Connect(_connectionString); _redis = _multiplexer.GetDatabase(); + _removeComplete = removeComplete; } public async Task CreateNewWorkflow(WorkflowInstance workflow) @@ -50,7 +52,11 @@ public async Task PersistWorkflow(WorkflowInstance workflow) if ((workflow.Status == WorkflowStatus.Runnable) && (workflow.NextExecution.HasValue)) await _redis.SortedSetAddAsync($"{_prefix}.{WORKFLOW_SET}.{RUNNABLE_INDEX}", workflow.Id, workflow.NextExecution.Value); else + { await _redis.SortedSetRemoveAsync($"{_prefix}.{WORKFLOW_SET}.{RUNNABLE_INDEX}", workflow.Id); + if (workflow.Status == WorkflowStatus.Complete) + await _redis.HashDeleteAsync($"{_prefix}.{WORKFLOW_SET}", workflow.Id); + } } public async Task> GetRunnableInstances(DateTime asAt) diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index 4513eb506..bdb8c6456 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,14 +2,14 @@ netstandard2.0 - 3.0.1 + 3.0.2 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git https://github.com/danielgerlag/workflow-core Redis providers for Workflow Core (Persistence, queueing, distributed locking and event hubs) - 3.0.1 + 3.0.2 diff --git a/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs index 105e5900a..cb68f7753 100644 --- a/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs @@ -26,7 +26,7 @@ protected override IPersistenceProvider Subject { if (_subject == null) { - var client = new RedisPersistenceProvider(RedisDockerSetup.ConnectionString, "test", new LoggerFactory()); + var client = new RedisPersistenceProvider(RedisDockerSetup.ConnectionString, "test", false, new LoggerFactory()); client.EnsureStoreExists(); _subject = client; } From f7b3f8781ff15aeee93e679091fb1aa42a24fc5e Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 2 Aug 2020 08:12:02 -0700 Subject: [PATCH 219/462] check flag --- .../Services/RedisPersistenceProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index ad92848e5..79a15b970 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -54,7 +54,7 @@ public async Task PersistWorkflow(WorkflowInstance workflow) else { await _redis.SortedSetRemoveAsync($"{_prefix}.{WORKFLOW_SET}.{RUNNABLE_INDEX}", workflow.Id); - if (workflow.Status == WorkflowStatus.Complete) + if (_removeComplete && workflow.Status == WorkflowStatus.Complete) await _redis.HashDeleteAsync($"{_prefix}.{WORKFLOW_SET}", workflow.Id); } } From 15b5dc3fa40aa22225d991e9e16472f26d7ba5fb Mon Sep 17 00:00:00 2001 From: kirsanium Date: Sat, 11 Jul 2020 02:29:50 +0700 Subject: [PATCH 220/462] add possibility to not use `WorkflowBuilder.StartWith()` every time --- src/WorkflowCore/Interface/IStepBuilder.cs | 140 +--------------- .../Interface/IWorkflowBuilder.cs | 2 +- .../Interface/IWorkflowModifier.cs | 149 ++++++++++++++++++ .../FluentBuilders/WorkflowBuilder.cs | 97 +++++++++++- 4 files changed, 246 insertions(+), 142 deletions(-) create mode 100644 src/WorkflowCore/Interface/IWorkflowModifier.cs diff --git a/src/WorkflowCore/Interface/IStepBuilder.cs b/src/WorkflowCore/Interface/IStepBuilder.cs index d5ebcec7b..c654a9d20 100644 --- a/src/WorkflowCore/Interface/IStepBuilder.cs +++ b/src/WorkflowCore/Interface/IStepBuilder.cs @@ -6,7 +6,7 @@ namespace WorkflowCore.Interface { - public interface IStepBuilder + public interface IStepBuilder : IWorkflowModifier where TStepBody : IStepBody { @@ -28,36 +28,6 @@ public interface IStepBuilder /// IStepBuilder Id(string id); - /// - /// Specify the next step in the workflow - /// - /// The type of the step to execute - /// Configure additional parameters for this step - /// - IStepBuilder Then(Action> stepSetup = null) where TStep : IStepBody; - - /// - /// Specify the next step in the workflow - /// - /// - /// - /// - IStepBuilder Then(IStepBuilder newStep) where TStep : IStepBody; - - /// - /// Specify an inline next step in the workflow - /// - /// - /// - IStepBuilder Then(Func body); - - /// - /// Specify an inline next step in the workflow - /// - /// - /// - IStepBuilder Then(Action body); - /// /// Specify the next step in the workflow by Id /// @@ -129,26 +99,6 @@ public interface IStepBuilder /// IStepBuilder Output(Action action); - /// - /// Wait here until to specified event is published - /// - /// The name used to identify the kind of event to wait for - /// A specific key value within the context of the event to wait for - /// Listen for events as of this effective date - /// A conditon that when true will cancel this WaitFor - /// - IStepBuilder WaitFor(string eventName, Expression> eventKey, Expression> effectiveDate = null, Expression> cancelCondition = null); - - /// - /// Wait here until to specified event is published - /// - /// The name used to identify the kind of event to wait for - /// A specific key value within the context of the event to wait for - /// Listen for events as of this effective date - /// A conditon that when true will cancel this WaitFor - /// - IStepBuilder WaitFor(string eventName, Expression> eventKey, Expression> effectiveDate = null, Expression> cancelCondition = null); - IStepBuilder End(string name) where TStep : IStepBody; /// @@ -165,83 +115,6 @@ public interface IStepBuilder /// IStepBuilder EndWorkflow(); - /// - /// Wait for a specified period - /// - /// - /// - IStepBuilder Delay(Expression> period); - - /// - /// Evaluate an expression and take a different path depending on the value - /// - /// Expression to evaluate for decision - /// - IStepBuilder Decide(Expression> expression); - - /// - /// Execute a block of steps, once for each item in a collection in a parallel foreach - /// - /// Resolves a collection for iterate over - /// - IContainerStepBuilder ForEach(Expression> collection); - - /// - /// Execute a block of steps, once for each item in a collection in a RunParallel foreach - /// - /// Resolves a collection for iterate over - /// - IContainerStepBuilder ForEach(Expression> collection, Expression> runParallel); - - /// - /// Repeat a block of steps until a condition becomes true - /// - /// Resolves a condition to break out of the while loop - /// - IContainerStepBuilder While(Expression> condition); - - /// - /// Execute a block of steps if a condition is true - /// - /// Resolves a condition to evaluate - /// - IContainerStepBuilder If(Expression> condition); - - /// - /// Configure an outcome for this step, then wire it to a sequence - /// - /// - /// - IContainerStepBuilder When(Expression> outcomeValue, string label = null); - - /// - /// Execute multiple blocks of steps in parallel - /// - /// - IParallelStepBuilder Parallel(); - - /// - /// Execute a sequence of steps in a container - /// - /// - IStepBuilder Saga(Action> builder); - - /// - /// Schedule a block of steps to execute in parallel sometime in the future - /// - /// The time span to wait before executing the block - /// - IContainerStepBuilder Schedule(Expression> time); - - /// - /// Schedule a block of steps to execute in parallel sometime in the future at a recurring interval - /// - /// The time span to wait between recurring executions - /// Resolves a condition to stop the recurring task - /// - IContainerStepBuilder Recur(Expression> interval, Expression> until); - - /// /// Undo step if unhandled exception is thrown by this step /// @@ -277,16 +150,5 @@ public interface IStepBuilder /// /// IStepBuilder CancelCondition(Expression> cancelCondition, bool proceedAfterCancel = false); - - /// - /// Wait here until an external activity is complete - /// - /// The name used to identify the activity to wait for - /// The data to pass the external activity worker - /// Listen for events as of this effective date - /// A conditon that when true will cancel this WaitFor - /// - IStepBuilder Activity(string activityName, Expression> parameters = null, Expression> effectiveDate = null, Expression> cancelCondition = null); - } } diff --git a/src/WorkflowCore/Interface/IWorkflowBuilder.cs b/src/WorkflowCore/Interface/IWorkflowBuilder.cs index 404a359f3..ec7b7bad2 100644 --- a/src/WorkflowCore/Interface/IWorkflowBuilder.cs +++ b/src/WorkflowCore/Interface/IWorkflowBuilder.cs @@ -20,7 +20,7 @@ public interface IWorkflowBuilder void AttachBranch(IWorkflowBuilder branch); } - public interface IWorkflowBuilder : IWorkflowBuilder + public interface IWorkflowBuilder : IWorkflowBuilder, IWorkflowModifier { IStepBuilder StartWith(Action> stepSetup = null) where TStep : IStepBody; diff --git a/src/WorkflowCore/Interface/IWorkflowModifier.cs b/src/WorkflowCore/Interface/IWorkflowModifier.cs new file mode 100644 index 000000000..963e17af6 --- /dev/null +++ b/src/WorkflowCore/Interface/IWorkflowModifier.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections; +using System.Linq.Expressions; +using WorkflowCore.Models; +using WorkflowCore.Primitives; + +namespace WorkflowCore.Interface +{ + public interface IWorkflowModifier + where TStepBody : IStepBody + + { + /// + /// Specify the next step in the workflow + /// + /// The type of the step to execute + /// Configure additional parameters for this step + /// + IStepBuilder Then(Action> stepSetup = null) where TStep : IStepBody; + + /// + /// Specify the next step in the workflow + /// + /// + /// + /// + IStepBuilder Then(IStepBuilder newStep) where TStep : IStepBody; + + /// + /// Specify an inline next step in the workflow + /// + /// + /// + IStepBuilder Then(Func body); + + /// + /// Specify an inline next step in the workflow + /// + /// + /// + IStepBuilder Then(Action body); + + /// + /// Wait here until to specified event is published + /// + /// The name used to identify the kind of event to wait for + /// A specific key value within the context of the event to wait for + /// Listen for events as of this effective date + /// A conditon that when true will cancel this WaitFor + /// + IStepBuilder WaitFor(string eventName, Expression> eventKey, + Expression> effectiveDate = null, Expression> cancelCondition = null); + + /// + /// Wait here until to specified event is published + /// + /// The name used to identify the kind of event to wait for + /// A specific key value within the context of the event to wait for + /// Listen for events as of this effective date + /// A conditon that when true will cancel this WaitFor + /// + IStepBuilder WaitFor(string eventName, + Expression> eventKey, + Expression> effectiveDate = null, Expression> cancelCondition = null); + + /// + /// Wait for a specified period + /// + /// + /// + IStepBuilder Delay(Expression> period); + + /// + /// Evaluate an expression and take a different path depending on the value + /// + /// Expression to evaluate for decision + /// + IStepBuilder Decide(Expression> expression); + + /// + /// Execute a block of steps, once for each item in a collection in a parallel foreach + /// + /// Resolves a collection for iterate over + /// + IContainerStepBuilder ForEach(Expression> collection); + + /// + /// Repeat a block of steps until a condition becomes true + /// + /// Resolves a condition to break out of the while loop + /// + IContainerStepBuilder While(Expression> condition); + + /// + /// Execute a block of steps if a condition is true + /// + /// Resolves a condition to evaluate + /// + IContainerStepBuilder If(Expression> condition); + + /// + /// Configure an outcome for this step, then wire it to a sequence + /// + /// + /// + IContainerStepBuilder When(Expression> outcomeValue, + string label = null); + + /// + /// Execute multiple blocks of steps in parallel + /// + /// + IParallelStepBuilder Parallel(); + + /// + /// Execute a sequence of steps in a container + /// + /// + IStepBuilder Saga(Action> builder); + + /// + /// Schedule a block of steps to execute in parallel sometime in the future + /// + /// The time span to wait before executing the block + /// + IContainerStepBuilder Schedule(Expression> time); + + /// + /// Schedule a block of steps to execute in parallel sometime in the future at a recurring interval + /// + /// The time span to wait between recurring executions + /// Resolves a condition to stop the recurring task + /// + IContainerStepBuilder Recur(Expression> interval, + Expression> until); + + /// + /// Wait here until an external activity is complete + /// + /// The name used to identify the activity to wait for + /// The data to pass the external activity worker + /// Listen for events as of this effective date + /// A conditon that when true will cancel this WaitFor + /// + IStepBuilder Activity(string activityName, Expression> parameters = null, + Expression> effectiveDate = null, Expression> cancelCondition = null); + + } +} \ No newline at end of file diff --git a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs index 2621f80d3..e5c7faef3 100644 --- a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs @@ -1,6 +1,8 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Primitives; @@ -148,7 +150,98 @@ public IWorkflowBuilder CreateBranch() var result = new WorkflowBuilder(new List()); return result; } - + + public IStepBuilder Then(Action> stepSetup = null) where TStep : IStepBody + { + return Start().Then(stepSetup); + } + + public IStepBuilder Then(IStepBuilder newStep) where TStep : IStepBody + { + return Start().Then(newStep); + } + + public IStepBuilder Then(Func body) + { + return Start().Then(body); + } + + public IStepBuilder Then(Action body) + { + return Start().Then(body); + } + + public IStepBuilder WaitFor(string eventName, Expression> eventKey, Expression> effectiveDate = null, + Expression> cancelCondition = null) + { + return Start().WaitFor(eventName, eventKey, effectiveDate, cancelCondition); + } + + public IStepBuilder WaitFor(string eventName, Expression> eventKey, Expression> effectiveDate = null, + Expression> cancelCondition = null) + { + return Start().WaitFor(eventName, eventKey, effectiveDate, cancelCondition); + } + + public IStepBuilder Delay(Expression> period) + { + return Start().Delay(period); + } + + public IStepBuilder Decide(Expression> expression) + { + return Start().Decide(expression); + } + + public IContainerStepBuilder ForEach(Expression> collection) + { + return Start().ForEach(collection); + } + + public IContainerStepBuilder While(Expression> condition) + { + return Start().While(condition); + } + + public IContainerStepBuilder If(Expression> condition) + { + return Start().If(condition); + } + + public IContainerStepBuilder When(Expression> outcomeValue, string label = null) + { + return ((IWorkflowModifier) Start()).When(outcomeValue, label); + } + + public IParallelStepBuilder Parallel() + { + return Start().Parallel(); + } + + public IStepBuilder Saga(Action> builder) + { + return Start().Saga(builder); + } + + public IContainerStepBuilder Schedule(Expression> time) + { + return Start().Schedule(time); + } + + public IContainerStepBuilder Recur(Expression> interval, Expression> until) + { + return Start().Recur(interval, until); + } + + public IStepBuilder Activity(string activityName, Expression> parameters = null, Expression> effectiveDate = null, + Expression> cancelCondition = null) + { + return Start().Activity(activityName, parameters, effectiveDate, cancelCondition); + } + + private IStepBuilder Start() + { + return StartWith(_ => ExecutionResult.Next()); + } } - } From 28035febbae6316049eadc588e67b8c646297f05 Mon Sep 17 00:00:00 2001 From: kirsanium Date: Mon, 3 Aug 2020 06:58:53 +0700 Subject: [PATCH 221/462] fix conflicts --- src/WorkflowCore/Interface/IWorkflowModifier.cs | 7 +++++++ .../Services/FluentBuilders/WorkflowBuilder.cs | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/WorkflowCore/Interface/IWorkflowModifier.cs b/src/WorkflowCore/Interface/IWorkflowModifier.cs index 963e17af6..d8af82616 100644 --- a/src/WorkflowCore/Interface/IWorkflowModifier.cs +++ b/src/WorkflowCore/Interface/IWorkflowModifier.cs @@ -83,6 +83,13 @@ IStepBuilder WaitFor(string eventName, /// Resolves a collection for iterate over /// IContainerStepBuilder ForEach(Expression> collection); + + /// + /// Execute a block of steps, once for each item in a collection in a RunParallel foreach + /// + /// Resolves a collection for iterate over + /// + IContainerStepBuilder ForEach(Expression> collection, Expression> runParallel); /// /// Repeat a block of steps until a condition becomes true diff --git a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs index e5c7faef3..0013118a7 100644 --- a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs @@ -197,6 +197,11 @@ public IContainerStepBuilder ForEach(Expression ForEach(Expression> collection, Expression> runParallel) + { + return Start().ForEach(collection, runParallel); + } public IContainerStepBuilder While(Expression> condition) { From c7f8d8c8ad1eb672e1de6435f54fb84893da91d1 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 8 Aug 2020 08:16:37 -0700 Subject: [PATCH 222/462] remove object pooling --- WorkflowCore.sln | 9 ++- src/WorkflowCore/Models/WorkflowOptions.cs | 2 +- .../BackgroundTasks/WorkflowConsumer.cs | 70 ++++++++----------- test/ScratchPad/Program.cs | 36 +++++++--- test/ScratchPad/ScratchPad.csproj | 10 +-- 5 files changed, 73 insertions(+), 54 deletions(-) diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 81030f058..5134c23dc 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -141,7 +141,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.DSL", "src\Wor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample18", "src\samples\WorkflowCore.Sample18\WorkflowCore.Sample18.csproj", "{5BE6D628-B9DB-4C76-AAEB-8F3800509A84}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample09s", "src\samples\WorkflowCore.Sample09s\WorkflowCore.Sample09s.csproj", "{E32CF21A-29CC-46D1-8044-FCC327F2B281}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample09s", "src\samples\WorkflowCore.Sample09s\WorkflowCore.Sample09s.csproj", "{E32CF21A-29CC-46D1-8044-FCC327F2B281}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchPad\ScratchPad.csproj", "{51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -349,6 +351,10 @@ Global {E32CF21A-29CC-46D1-8044-FCC327F2B281}.Debug|Any CPU.Build.0 = Debug|Any CPU {E32CF21A-29CC-46D1-8044-FCC327F2B281}.Release|Any CPU.ActiveCfg = Release|Any CPU {E32CF21A-29CC-46D1-8044-FCC327F2B281}.Release|Any CPU.Build.0 = Release|Any CPU + {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -407,6 +413,7 @@ Global {20B98905-08CB-4854-8E2C-A31A078383E9} = {EF47161E-E399-451C-BDE8-E92AAD3BD761} {5BE6D628-B9DB-4C76-AAEB-8F3800509A84} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {E32CF21A-29CC-46D1-8044-FCC327F2B281} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} + {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index 635223af5..44f1f1375 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -16,7 +16,7 @@ public class WorkflowOptions internal TimeSpan PollInterval; internal TimeSpan IdleTime; internal TimeSpan ErrorRetryInterval; - internal int MaxConcurrentWorkflows = Math.Max(Environment.ProcessorCount, 2); + internal int MaxConcurrentWorkflows = Math.Max(Environment.ProcessorCount, 4); public IServiceCollection Services { get; private set; } diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index f9222c715..b21c7ec72 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -12,17 +12,17 @@ internal class WorkflowConsumer : QueueConsumer, IBackgroundTask { private readonly IDistributedLockProvider _lockProvider; private readonly IDateTimeProvider _datetimeProvider; - private readonly ObjectPool _persistenceStorePool; - private readonly ObjectPool _executorPool; + private readonly IPersistenceProvider _persistenceStore; + private readonly IWorkflowExecutor _executor; protected override int MaxConcurrentItems => Options.MaxConcurrentWorkflows; protected override QueueType Queue => QueueType.Workflow; - public WorkflowConsumer(IPooledObjectPolicy persistencePoolPolicy, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IPooledObjectPolicy executorPoolPolicy, IDateTimeProvider datetimeProvider, WorkflowOptions options) + public WorkflowConsumer(IPersistenceProvider persistenceProvider, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IWorkflowExecutor executor, IDateTimeProvider datetimeProvider, WorkflowOptions options) : base(queueProvider, loggerFactory, options) { - _persistenceStorePool = new DefaultObjectPool(persistencePoolPolicy); - _executorPool = new DefaultObjectPool(executorPoolPolicy); + _persistenceStore = persistenceProvider; + _executor = executor; _lockProvider = lockProvider; _datetimeProvider = datetimeProvider; } @@ -37,53 +37,45 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance WorkflowInstance workflow = null; WorkflowExecutorResult result = null; - var persistenceStore = _persistenceStorePool.Get(); + try { - try - { - cancellationToken.ThrowIfCancellationRequested(); - workflow = await persistenceStore.GetWorkflowInstance(itemId); - if (workflow.Status == WorkflowStatus.Runnable) + cancellationToken.ThrowIfCancellationRequested(); + workflow = await _persistenceStore.GetWorkflowInstance(itemId); + if (workflow.Status == WorkflowStatus.Runnable) + { + try + { + result = await _executor.Execute(workflow); + } + finally { - var executor = _executorPool.Get(); - try - { - result = await executor.Execute(workflow); - } - finally - { - _executorPool.Return(executor); - await persistenceStore.PersistWorkflow(workflow); - await QueueProvider.QueueWork(itemId, QueueType.Index); - } + await _persistenceStore.PersistWorkflow(workflow); + await QueueProvider.QueueWork(itemId, QueueType.Index); } } - finally + } + finally + { + await _lockProvider.ReleaseLock(itemId); + if ((workflow != null) && (result != null)) { - await _lockProvider.ReleaseLock(itemId); - if ((workflow != null) && (result != null)) + foreach (var sub in result.Subscriptions) { - foreach (var sub in result.Subscriptions) - { - await SubscribeEvent(sub, persistenceStore); - } + await SubscribeEvent(sub, _persistenceStore); + } - await persistenceStore.PersistErrors(result.Errors); + await _persistenceStore.PersistErrors(result.Errors); - var readAheadTicks = _datetimeProvider.UtcNow.Add(Options.PollInterval).Ticks; + var readAheadTicks = _datetimeProvider.UtcNow.Add(Options.PollInterval).Ticks; - if ((workflow.Status == WorkflowStatus.Runnable) && workflow.NextExecution.HasValue && workflow.NextExecution.Value < readAheadTicks) - { - new Task(() => FutureQueue(workflow, cancellationToken)).Start(); - } + if ((workflow.Status == WorkflowStatus.Runnable) && workflow.NextExecution.HasValue && workflow.NextExecution.Value < readAheadTicks) + { + new Task(() => FutureQueue(workflow, cancellationToken)).Start(); } } } - finally - { - _persistenceStorePool.Return(persistenceStore); - } + } private async Task SubscribeEvent(EventSubscription subscription, IPersistenceProvider persistenceStore) diff --git a/test/ScratchPad/Program.cs b/test/ScratchPad/Program.cs index 40c569a29..08f383712 100644 --- a/test/ScratchPad/Program.cs +++ b/test/ScratchPad/Program.cs @@ -26,15 +26,30 @@ public static void Main(string[] args) //loader.LoadDefinition(Properties.Resources.HelloWorld, Deserializers.Json); host.Start(); - - host.StartWorkflow("Test01", 1, new WfData() + + var ids = new List(); + //for (var i = 0; i < 12000; i++) + //{ + // var wid = host.StartWorkflow("Test01", 1, new WfData() { Value1 = "two", Value2 = "data2" }).Result; + // ids.Add(wid); + //} + //Console.WriteLine("started..."); + //Thread.Sleep(5000); + + host.PublishEvent("MyEvent", "Key", "one", DateTime.Now); + + for (var i = 0; i < 12000; i++) { - Value1 = "two", - Value2 = "data2" - }); + var wid = host.StartWorkflow("Test01", 1, new WfData() { Value1 = "two", Value2 = "data2" }).Result; + ids.Add(wid); + } + + Console.WriteLine("started2..."); + Thread.Sleep(5000); + + host.PublishEvent("MyEvent", "Key", "one", DateTime.Now); + - - Console.ReadLine(); host.Stop(); } @@ -46,8 +61,11 @@ private static IServiceProvider ConfigureServices() services.AddLogging(); //services.AddWorkflow(); //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); + services.AddWorkflow(cfg => { + cfg.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true); + cfg.UseMaxConcurrentWorkflows(100); //var ddbConfig = new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest2 }; //cfg.UseAwsDynamoPersistence(new EnvironmentVariablesAWSCredentials(), ddbConfig, "elastic"); //cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://localhost:9200")), "workflows"); @@ -111,10 +129,12 @@ public void Build(IWorkflowBuilder builder) builder .StartWith() + .WaitFor("MyEvent", (data, context) => "Key", data => DateTime.Now) + .Output(data => data.Value1, step => step.EventData) .Then((context) => { Console.WriteLine("------1"); - Task.Delay(TimeSpan.FromSeconds(20)).Wait(); + Task.Delay(TimeSpan.FromSeconds(5)).Wait(); Console.WriteLine("------2"); return ExecutionResult.Next(); }) diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 548b8fa61..4fb61dd24 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -10,6 +10,11 @@ false + + + + + @@ -23,11 +28,6 @@ - - - - - True From ffca3da9d045520529f517b450ef910df7265500 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 8 Aug 2020 08:17:55 -0700 Subject: [PATCH 223/462] bump version --- src/WorkflowCore/WorkflowCore.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index b4748923d..9554b92f9 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,9 +15,9 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.1.6 - 3.1.6.0 - 3.1.6.0 + 3.2.0 + 3.2.0.0 + 3.2.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png 3.1.6 From 2127e6fa45cc191d0c2de363eb7c7673f8c8a476 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 8 Aug 2020 08:44:05 -0700 Subject: [PATCH 224/462] shutdown concurrency issue --- .../Services/BackgroundTasks/QueueConsumer.cs | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 54d384879..0d1a5474a 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -61,7 +61,12 @@ private async void Execute() { try { - if (activeTasks.Count >= MaxConcurrentItems) + var activeCount = 0; + lock (activeTasks) + { + activeCount = activeTasks.Count; + } + if (activeCount >= MaxConcurrentItems) { await Task.Delay(Options.IdleTime); continue; @@ -75,14 +80,19 @@ private async void Execute() await Task.Delay(Options.IdleTime, cancelToken); continue; } - - if (activeTasks.ContainsKey(item)) + + var hasTask = false; + lock (activeTasks) + { + hasTask = activeTasks.ContainsKey(item); + } + if (hasTask) { secondPasses.Add(item); if (!EnableSecondPasses) await QueueProvider.QueueWork(item, Queue); continue; - } + } secondPasses.TryRemove(item); @@ -121,7 +131,13 @@ private async void Execute() } } - foreach (var task in activeTasks.Values) + List toComplete; + lock (activeTasks) + { + toComplete = activeTasks.Values.ToList(); + } + + foreach (var task in toComplete) task.Wait(); } From f8121321a88b8ca0a212bc064eff73b8d4b9b431 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 8 Aug 2020 08:53:28 -0700 Subject: [PATCH 225/462] package version --- src/WorkflowCore/WorkflowCore.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 9554b92f9..0b8e610f8 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -20,7 +20,7 @@ 3.2.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.1.6 + 3.2.0 From 0b50ab7956ef95c1aeec419244d9b4733e9975e5 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 8 Aug 2020 19:57:02 -0700 Subject: [PATCH 226/462] performance tweaks for event bursts --- src/WorkflowCore/Interface/IGreyList.cs | 9 +++ .../ServiceCollectionExtensions.cs | 1 + .../Services/BackgroundTasks/EventConsumer.cs | 13 +++- .../BackgroundTasks/RunnablePoller.cs | 19 +++++- .../BackgroundTasks/WorkflowConsumer.cs | 7 ++- src/WorkflowCore/Services/GreyList.cs | 63 +++++++++++++++++++ src/WorkflowCore/WorkflowCore.csproj | 8 +-- 7 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 src/WorkflowCore/Interface/IGreyList.cs create mode 100644 src/WorkflowCore/Services/GreyList.cs diff --git a/src/WorkflowCore/Interface/IGreyList.cs b/src/WorkflowCore/Interface/IGreyList.cs new file mode 100644 index 000000000..3d2e0093a --- /dev/null +++ b/src/WorkflowCore/Interface/IGreyList.cs @@ -0,0 +1,9 @@ +namespace WorkflowCore.Interface +{ + public interface IGreyList + { + void Add(string id); + void Remove(string id); + bool Contains(string id); + } +} \ No newline at end of file diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index c241107c2..6fe511845 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -48,6 +48,7 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddTransient(); services.AddTransient(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index 06682431a..bdd730fca 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -16,13 +16,15 @@ internal class EventConsumer : QueueConsumer, IBackgroundTask private readonly IEventRepository _eventRepository; private readonly IDistributedLockProvider _lockProvider; private readonly IDateTimeProvider _datetimeProvider; + private readonly IGreyList _greylist; protected override int MaxConcurrentItems => 2; protected override QueueType Queue => QueueType.Event; - public EventConsumer(IWorkflowRepository workflowRepository, ISubscriptionRepository subscriptionRepository, IEventRepository eventRepository, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, WorkflowOptions options, IDateTimeProvider datetimeProvider) + public EventConsumer(IWorkflowRepository workflowRepository, ISubscriptionRepository subscriptionRepository, IEventRepository eventRepository, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, WorkflowOptions options, IDateTimeProvider datetimeProvider, IGreyList greylist) : base(queueProvider, loggerFactory, options) { _workflowRepository = workflowRepository; + _greylist = greylist; _subscriptionRepository = subscriptionRepository; _eventRepository = eventRepository; _lockProvider = lockProvider; @@ -41,6 +43,11 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { cancellationToken.ThrowIfCancellationRequested(); var evt = await _eventRepository.GetEvent(itemId); + if (evt.IsProcessed) + { + _greylist.Add($"evt:{evt.Id}"); + return; + } if (evt.EventTime <= _datetimeProvider.UtcNow) { IEnumerable subs = null; @@ -60,7 +67,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance subs = await _subscriptionRepository.GetSubscriptions(evt.EventName, evt.EventKey, evt.EventTime); } - var toQueue = new List(); + var toQueue = new HashSet(); var complete = true; foreach (var sub in subs.ToList()) @@ -79,7 +86,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance } } - private async Task SeedSubscription(Event evt, EventSubscription sub, List toQueue, CancellationToken cancellationToken) + private async Task SeedSubscription(Event evt, EventSubscription sub, HashSet toQueue, CancellationToken cancellationToken) { foreach (var eventId in await _eventRepository.GetEvents(sub.EventName, sub.EventKey, sub.SubscribeAsOf)) { diff --git a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs index 559c123f0..0800fe582 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs @@ -13,12 +13,14 @@ internal class RunnablePoller : IBackgroundTask private readonly IDistributedLockProvider _lockProvider; private readonly IQueueProvider _queueProvider; private readonly ILogger _logger; + private readonly IGreyList _greylist; private readonly WorkflowOptions _options; private Timer _pollTimer; - public RunnablePoller(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, WorkflowOptions options) + public RunnablePoller(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IGreyList greylist, WorkflowOptions options) { _persistenceStore = persistenceStore; + _greylist = greylist; _queueProvider = queueProvider; _logger = loggerFactory.CreateLogger(); _lockProvider = lockProvider; @@ -55,7 +57,13 @@ private async void PollRunnables(object target) var runnables = await _persistenceStore.GetRunnableInstances(DateTime.Now); foreach (var item in runnables) { + if (_greylist.Contains($"wf:{item}")) + { + _logger.LogDebug($"Got greylisted workflow {item}"); + continue; + } _logger.LogDebug("Got runnable instance {0}", item); + _greylist.Add($"wf:{item}"); await _queueProvider.QueueWork(item, QueueType.Workflow); } } @@ -80,8 +88,15 @@ private async void PollRunnables(object target) var events = await _persistenceStore.GetRunnableEvents(DateTime.Now); foreach (var item in events.ToList()) { + if (_greylist.Contains($"evt:{item}")) + { + _logger.LogDebug($"Got greylisted event {item}"); + _greylist.Add($"evt:{item}"); + continue; + } _logger.LogDebug($"Got unprocessed event {item}"); - await _queueProvider.QueueWork(item, QueueType.Event); + _greylist.Add($"evt:{item}"); + await _queueProvider.QueueWork(item, QueueType.Event); } } finally diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index b21c7ec72..5742270ce 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -14,14 +14,16 @@ internal class WorkflowConsumer : QueueConsumer, IBackgroundTask private readonly IDateTimeProvider _datetimeProvider; private readonly IPersistenceProvider _persistenceStore; private readonly IWorkflowExecutor _executor; + private readonly IGreyList _greylist; protected override int MaxConcurrentItems => Options.MaxConcurrentWorkflows; protected override QueueType Queue => QueueType.Workflow; - public WorkflowConsumer(IPersistenceProvider persistenceProvider, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IWorkflowExecutor executor, IDateTimeProvider datetimeProvider, WorkflowOptions options) + public WorkflowConsumer(IPersistenceProvider persistenceProvider, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IWorkflowExecutor executor, IDateTimeProvider datetimeProvider, IGreyList greylist, WorkflowOptions options) : base(queueProvider, loggerFactory, options) { _persistenceStore = persistenceProvider; + _greylist = greylist; _executor = executor; _lockProvider = lockProvider; _datetimeProvider = datetimeProvider; @@ -43,7 +45,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance cancellationToken.ThrowIfCancellationRequested(); workflow = await _persistenceStore.GetWorkflowInstance(itemId); if (workflow.Status == WorkflowStatus.Runnable) - { + { try { result = await _executor.Execute(workflow); @@ -52,6 +54,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { await _persistenceStore.PersistWorkflow(workflow); await QueueProvider.QueueWork(itemId, QueueType.Index); + _greylist.Remove($"wf:{itemId}"); } } } diff --git a/src/WorkflowCore/Services/GreyList.cs b/src/WorkflowCore/Services/GreyList.cs new file mode 100644 index 000000000..a697e2958 --- /dev/null +++ b/src/WorkflowCore/Services/GreyList.cs @@ -0,0 +1,63 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Threading; +using WorkflowCore.Interface; + +namespace WorkflowCore.Services +{ + public class GreyList : IGreyList, IDisposable + { + private readonly Timer _cycleTimer; + private readonly ConcurrentDictionary _list; + private readonly ILogger _logger; + private const int CYCLE_TIME = 600; + + public GreyList(ILoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + _list = new ConcurrentDictionary(); + _cycleTimer = new Timer(new TimerCallback(Cycle), null, TimeSpan.FromSeconds(CYCLE_TIME), TimeSpan.FromSeconds(CYCLE_TIME)); + } + + public void Add(string id) + { + _list.AddOrUpdate(id, DateTime.Now, (key, val) => DateTime.Now); + } + + public bool Contains(string id) + { + if (!_list.TryGetValue(id, out var start)) + return false; + + var result = start > (DateTime.Now.AddMinutes(-2)); + + if (!result) + _list.TryRemove(id, out var _); + + return result; + } + + private void Cycle(object target) + { + try + { + _list.Clear(); + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + } + } + + public void Dispose() + { + _cycleTimer.Dispose(); + } + + public void Remove(string id) + { + _list.TryRemove(id, out var _); + } + } +} diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 0b8e610f8..96d7bbeb3 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.2.0 - 3.2.0.0 - 3.2.0.0 + 3.2.1 + 3.2.1.0 + 3.2.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.2.0 + 3.2.1 From 906a7d779ec85e9249e6ccd0e7fdb4009001d1af Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 9 Aug 2020 08:53:08 -0700 Subject: [PATCH 227/462] tweaks --- src/WorkflowCore/Services/GreyList.cs | 7 ++++--- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/WorkflowCore/Services/GreyList.cs b/src/WorkflowCore/Services/GreyList.cs index a697e2958..ec1148291 100644 --- a/src/WorkflowCore/Services/GreyList.cs +++ b/src/WorkflowCore/Services/GreyList.cs @@ -11,13 +11,14 @@ public class GreyList : IGreyList, IDisposable private readonly Timer _cycleTimer; private readonly ConcurrentDictionary _list; private readonly ILogger _logger; - private const int CYCLE_TIME = 600; + private const int CYCLE_TIME = 30; + private const int TTL = 5; public GreyList(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(); _list = new ConcurrentDictionary(); - _cycleTimer = new Timer(new TimerCallback(Cycle), null, TimeSpan.FromSeconds(CYCLE_TIME), TimeSpan.FromSeconds(CYCLE_TIME)); + _cycleTimer = new Timer(new TimerCallback(Cycle), null, TimeSpan.FromMinutes(CYCLE_TIME), TimeSpan.FromMinutes(CYCLE_TIME)); } public void Add(string id) @@ -30,7 +31,7 @@ public bool Contains(string id) if (!_list.TryGetValue(id, out var start)) return false; - var result = start > (DateTime.Now.AddMinutes(-2)); + var result = start > (DateTime.Now.AddMinutes(-1 * TTL)); if (!result) _list.TryRemove(id, out var _); diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 96d7bbeb3..ae35416de 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.2.1 - 3.2.1.0 - 3.2.1.0 + 3.2.2 + 3.2.2.0 + 3.2.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.2.1 + 3.2.2 From 8d37f4ceeef228d5fb4df98ac81b7f8b44a230a4 Mon Sep 17 00:00:00 2001 From: Pavlo Korobov Date: Fri, 14 Aug 2020 21:34:39 +0200 Subject: [PATCH 228/462] Make IScopeProvider.cs aware of IStepExecutionContext --- src/WorkflowCore/Interface/IScopeProvider.cs | 2 +- src/WorkflowCore/Services/ScopeProvider.cs | 12 +++---- src/WorkflowCore/Services/WorkflowExecutor.cs | 20 +++++------ .../WorkflowCore.TestAssets.csproj | 2 +- .../Services/ScopeProviderTests.cs | 36 +++++++++++++++++++ .../Services/WorkflowExecutorFixture.cs | 7 +--- 6 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 test/WorkflowCore.UnitTests/Services/ScopeProviderTests.cs diff --git a/src/WorkflowCore/Interface/IScopeProvider.cs b/src/WorkflowCore/Interface/IScopeProvider.cs index c69351731..bab5f94d2 100644 --- a/src/WorkflowCore/Interface/IScopeProvider.cs +++ b/src/WorkflowCore/Interface/IScopeProvider.cs @@ -12,6 +12,6 @@ public interface IScopeProvider /// Create a new service scope /// /// - IServiceScope CreateScope(); + IServiceScope CreateScope(IStepExecutionContext context); } } \ No newline at end of file diff --git a/src/WorkflowCore/Services/ScopeProvider.cs b/src/WorkflowCore/Services/ScopeProvider.cs index ad1ae98d2..5c52d8eb9 100644 --- a/src/WorkflowCore/Services/ScopeProvider.cs +++ b/src/WorkflowCore/Services/ScopeProvider.cs @@ -6,20 +6,20 @@ namespace WorkflowCore.Services { /// /// A concrete implementation for the IScopeProvider interface - /// Largely to get around the problems of unit testing an extension method (CreateScope()) + /// Could be used for context-aware scope creation customization /// public class ScopeProvider : IScopeProvider { - private readonly IServiceProvider provider; + private readonly IServiceScopeFactory _serviceScopeFactory; - public ScopeProvider(IServiceProvider provider) + public ScopeProvider(IServiceScopeFactory serviceScopeFactory) { - this.provider = provider; + _serviceScopeFactory = serviceScopeFactory; } - public IServiceScope CreateScope() + public IServiceScope CreateScope(IStepExecutionContext context) { - return provider.CreateScope(); + return _serviceScopeFactory.CreateScope(); } } } diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 7597e7490..8d5507bae 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -137,7 +137,16 @@ private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, Workfl private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, ExecutionPointer pointer, WorkflowExecutorResult wfResult, WorkflowDefinition def) { - using (var scope = _scopeProvider.CreateScope()) + IStepExecutionContext context = new StepExecutionContext() + { + Workflow = workflow, + Step = step, + PersistenceData = pointer.PersistenceData, + ExecutionPointer = pointer, + Item = pointer.ContextItem + }; + + using (var scope = _scopeProvider.CreateScope(context)) { _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); @@ -157,15 +166,6 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe return; } - IStepExecutionContext context = new StepExecutionContext() - { - Workflow = workflow, - Step = step, - PersistenceData = pointer.PersistenceData, - ExecutionPointer = pointer, - Item = pointer.ContextItem - }; - foreach (var input in step.Inputs) input.AssignInput(workflow.Data, body, context); diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index 0105a6e9d..88cd82ea7 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -36,7 +36,7 @@ - + diff --git a/test/WorkflowCore.UnitTests/Services/ScopeProviderTests.cs b/test/WorkflowCore.UnitTests/Services/ScopeProviderTests.cs new file mode 100644 index 000000000..ab7869d1d --- /dev/null +++ b/test/WorkflowCore.UnitTests/Services/ScopeProviderTests.cs @@ -0,0 +1,36 @@ +using System; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using WorkflowCore.Interface; +using WorkflowCore.Services; +using Xunit; + +namespace WorkflowCore.UnitTests.Services +{ + public class ScopeProviderTests + { + private readonly ScopeProvider _sut; + private readonly Mock _scopeFactoryMock; + + public ScopeProviderTests() + { + _scopeFactoryMock = new Mock(); + + _sut = new ScopeProvider(_scopeFactoryMock.Object); + } + + [Fact(DisplayName = "Should return IServiceScope")] + public void ReturnsServiceScope_CreateScopeCalled() + { + var scope = new Mock().Object; + _scopeFactoryMock.Setup(x => x.CreateScope()) + .Returns(scope); + + var result = _sut.CreateScope(new Mock().Object); + + result.Should().NotBeNull().And.BeSameAs(scope); + _scopeFactoryMock.Verify(x => x.CreateScope(), Times.Once); + } + } +} \ No newline at end of file diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 6c99586f6..622af03ff 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -3,15 +3,10 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Services; -using FluentAssertions; using Xunit; -using WorkflowCore.Primitives; -using System.Linq.Expressions; -using System.Threading.Tasks; namespace WorkflowCore.UnitTests.Services { @@ -44,7 +39,7 @@ public WorkflowExecutorFixture() Options = new WorkflowOptions(A.Fake()); var scope = A.Fake(); - A.CallTo(() => ScopeProvider.CreateScope()).Returns(scope); + A.CallTo(() => ScopeProvider.CreateScope(A._)).Returns(scope); A.CallTo(() => scope.ServiceProvider).Returns(ServiceProvider); A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); From 486a7b6722e36a88b8e3913eb4576a61d0e6015c Mon Sep 17 00:00:00 2001 From: Pavlo Korobov Date: Fri, 14 Aug 2020 22:07:29 +0200 Subject: [PATCH 229/462] Make IScopeProvider.cs aware of IStepExecutionContext --- test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index 88cd82ea7..12da1f57b 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -36,7 +36,7 @@ - + From 8f0c2deab73fc14be5500b7d0734ad18033ec67b Mon Sep 17 00:00:00 2001 From: Alexander Kulik Date: Sun, 6 Sep 2020 12:34:29 +0300 Subject: [PATCH 230/462] Pass the CancellationToken into IStepExecutionContext for async steps. --- src/WorkflowCore/Interface/IStepExecutionContext.cs | 7 +++++-- src/WorkflowCore/Interface/IWorkflowExecutor.cs | 5 +++-- src/WorkflowCore/Models/StepExecutionContext.cs | 3 +++ .../Services/BackgroundTasks/WorkflowConsumer.cs | 2 +- src/WorkflowCore/Services/WorkflowExecutor.cs | 10 ++++++---- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/WorkflowCore/Interface/IStepExecutionContext.cs b/src/WorkflowCore/Interface/IStepExecutionContext.cs index e59198a13..7aab7123c 100644 --- a/src/WorkflowCore/Interface/IStepExecutionContext.cs +++ b/src/WorkflowCore/Interface/IStepExecutionContext.cs @@ -1,4 +1,5 @@ -using WorkflowCore.Models; +using System.Threading; +using WorkflowCore.Models; namespace WorkflowCore.Interface { @@ -12,6 +13,8 @@ public interface IStepExecutionContext WorkflowStep Step { get; set; } - WorkflowInstance Workflow { get; set; } + WorkflowInstance Workflow { get; set; } + + CancellationToken CancellationToken { get; set; } } } \ No newline at end of file diff --git a/src/WorkflowCore/Interface/IWorkflowExecutor.cs b/src/WorkflowCore/Interface/IWorkflowExecutor.cs index 798057fdc..d0df49d40 100644 --- a/src/WorkflowCore/Interface/IWorkflowExecutor.cs +++ b/src/WorkflowCore/Interface/IWorkflowExecutor.cs @@ -1,10 +1,11 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; using WorkflowCore.Models; namespace WorkflowCore.Interface { public interface IWorkflowExecutor { - Task Execute(WorkflowInstance workflow); + Task Execute(WorkflowInstance workflow, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/WorkflowCore/Models/StepExecutionContext.cs b/src/WorkflowCore/Models/StepExecutionContext.cs index b48bcd35c..096d719f3 100644 --- a/src/WorkflowCore/Models/StepExecutionContext.cs +++ b/src/WorkflowCore/Models/StepExecutionContext.cs @@ -1,4 +1,5 @@ using WorkflowCore.Interface; +using System.Threading; namespace WorkflowCore.Models { @@ -13,5 +14,7 @@ public class StepExecutionContext : IStepExecutionContext public object PersistenceData { get; set; } public object Item { get; set; } + + public CancellationToken CancellationToken { get; set; } = CancellationToken.None; } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 5742270ce..6ce3776b5 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -48,7 +48,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { try { - result = await _executor.Execute(workflow); + result = await _executor.Execute(workflow, cancellationToken); } finally { diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 8d5507bae..e557a2bb2 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; @@ -37,7 +38,7 @@ public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProv _executionResultProcessor = executionResultProcessor; } - public async Task Execute(WorkflowInstance workflow) + public async Task Execute(WorkflowInstance workflow, CancellationToken cancellationToken = default) { var wfResult = new WorkflowExecutorResult(); @@ -76,7 +77,7 @@ public async Task Execute(WorkflowInstance workflow) if (!InitializeStep(workflow, step, wfResult, def, pointer)) continue; - await ExecuteStep(workflow, step, pointer, wfResult, def); + await ExecuteStep(workflow, step, pointer, wfResult, def, cancellationToken); } catch (Exception ex) { @@ -135,7 +136,7 @@ private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, Workfl return true; } - private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, ExecutionPointer pointer, WorkflowExecutorResult wfResult, WorkflowDefinition def) + private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, ExecutionPointer pointer, WorkflowExecutorResult wfResult, WorkflowDefinition def, CancellationToken cancellationToken = default) { IStepExecutionContext context = new StepExecutionContext() { @@ -143,7 +144,8 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe Step = step, PersistenceData = pointer.PersistenceData, ExecutionPointer = pointer, - Item = pointer.ContextItem + Item = pointer.ContextItem, + CancellationToken = cancellationToken }; using (var scope = _scopeProvider.CreateScope(context)) From 9732cc4c94bddae85b4566dd772fea7944aca101 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 7 Sep 2020 12:31:55 -0700 Subject: [PATCH 231/462] issue #635 --- .../FluentBuilders/WorkflowBuilder.cs | 28 +++++++++++++++++++ src/WorkflowCore/WorkflowCore.csproj | 8 +++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs index 0013118a7..f5acd7694 100644 --- a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs @@ -65,6 +65,28 @@ public void AttachBranch(IWorkflowBuilder branch) if (Branches.Contains(branch)) return; + var branchStart = LastStep + branch.LastStep + 1; + + foreach (var step in branch.Steps) + { + var oldId = step.Id; + step.Id = oldId + branchStart; + foreach (var step2 in branch.Steps) + { + foreach (var outcome in step2.Outcomes) + { + if (outcome.NextStep == oldId) + outcome.NextStep = step.Id; + } + + for (var i = 0; i < step2.Children.Count; i++) + { + if (step2.Children[i] == oldId) + step2.Children[i] = step.Id; + } + } + } + foreach (var step in branch.Steps) { var oldId = step.Id; @@ -76,6 +98,12 @@ public void AttachBranch(IWorkflowBuilder branch) if (outcome.NextStep == oldId) outcome.NextStep = step.Id; } + + for (var i = 0; i < step2.Children.Count; i++) + { + if (step2.Children[i] == oldId) + step2.Children[i] = step.Id; + } } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index ae35416de..68e457261 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.2.2 - 3.2.2.0 - 3.2.2.0 + 3.2.3 + 3.2.3.0 + 3.2.3.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.2.2 + 3.2.3 From 18be33b9b204301e2118d1d811d049c744f1f4b3 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 13 Sep 2020 19:47:20 -0700 Subject: [PATCH 232/462] async issue --- .../Services/BackgroundTasks/QueueConsumer.cs | 6 ++++-- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 0d1a5474a..69c146644 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -141,11 +141,11 @@ private async void Execute() task.Wait(); } - private async Task ExecuteItem(string itemId) + private Task ExecuteItem(string itemId) { try { - await ProcessItem(itemId, _cancellationTokenSource.Token); + ProcessItem(itemId, _cancellationTokenSource.Token).Wait(); } catch (OperationCanceledException) { @@ -155,6 +155,8 @@ private async Task ExecuteItem(string itemId) { Logger.LogError(default(EventId), ex, $"Error executing item {itemId} - {ex.Message}"); } + + return Task.CompletedTask; } } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 68e457261..b1e8c48df 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.2.3 - 3.2.3.0 - 3.2.3.0 + 3.2.4 + 3.2.4.0 + 3.2.4.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.2.3 + 3.2.4 From b8af93ab05213dbd3e9973d919594d26f75c0889 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 16 Sep 2020 07:40:42 -0700 Subject: [PATCH 233/462] issue #645 --- .../Services/BackgroundTasks/QueueConsumer.cs | 86 +++++++++---------- src/WorkflowCore/WorkflowCore.csproj | 8 +- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 69c146644..b1bb7c5b9 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -21,12 +21,17 @@ internal abstract class QueueConsumer : IBackgroundTask protected readonly WorkflowOptions Options; protected Task DispatchTask; private CancellationTokenSource _cancellationTokenSource; + private Dictionary _activeTasks; + private ConcurrentHashSet _secondPasses; protected QueueConsumer(IQueueProvider queueProvider, ILoggerFactory loggerFactory, WorkflowOptions options) { QueueProvider = queueProvider; Options = options; Logger = loggerFactory.CreateLogger(GetType()); + + _activeTasks = new Dictionary(); + _secondPasses = new ConcurrentHashSet(); } protected abstract Task ProcessItem(string itemId, CancellationToken cancellationToken); @@ -39,9 +44,8 @@ public virtual void Start() } _cancellationTokenSource = new CancellationTokenSource(); - - DispatchTask = new Task(Execute, TaskCreationOptions.LongRunning); - DispatchTask.Start(); + + DispatchTask = Task.Factory.StartNew(Execute, TaskCreationOptions.LongRunning); } public virtual void Stop() @@ -51,20 +55,18 @@ public virtual void Stop() DispatchTask = null; } - private async void Execute() + private async Task Execute() { - var cancelToken = _cancellationTokenSource.Token; - var activeTasks = new Dictionary(); - var secondPasses = new ConcurrentHashSet(); + var cancelToken = _cancellationTokenSource.Token; while (!cancelToken.IsCancellationRequested) { try { var activeCount = 0; - lock (activeTasks) + lock (_activeTasks) { - activeCount = activeTasks.Count; + activeCount = _activeTasks.Count; } if (activeCount >= MaxConcurrentItems) { @@ -82,45 +84,26 @@ private async void Execute() } var hasTask = false; - lock (activeTasks) + lock (_activeTasks) { - hasTask = activeTasks.ContainsKey(item); + hasTask = _activeTasks.ContainsKey(item); } if (hasTask) { - secondPasses.Add(item); + _secondPasses.Add(item); if (!EnableSecondPasses) await QueueProvider.QueueWork(item, Queue); continue; } - secondPasses.TryRemove(item); + _secondPasses.TryRemove(item); - var task = new Task(async (object data) => - { - try - { - await ExecuteItem((string)data); - while (EnableSecondPasses && secondPasses.Contains(item)) - { - secondPasses.TryRemove(item); - await ExecuteItem((string)data); - } - } - finally - { - lock (activeTasks) - { - activeTasks.Remove((string)data); - } - } - }, item); - lock (activeTasks) + var waitHandle = new ManualResetEvent(false); + lock (_activeTasks) { - activeTasks.Add(item, task); + _activeTasks.Add(item, waitHandle); } - - task.Start(); + var task = ExecuteItem(item, waitHandle); } catch (OperationCanceledException) { @@ -131,21 +114,26 @@ private async void Execute() } } - List toComplete; - lock (activeTasks) + List toComplete; + lock (_activeTasks) { - toComplete = activeTasks.Values.ToList(); + toComplete = _activeTasks.Values.ToList(); } - - foreach (var task in toComplete) - task.Wait(); + + foreach (var handle in toComplete) + handle.WaitOne(); } - private Task ExecuteItem(string itemId) + private async Task ExecuteItem(string itemId, EventWaitHandle waitHandle) { try { - ProcessItem(itemId, _cancellationTokenSource.Token).Wait(); + await ProcessItem(itemId, _cancellationTokenSource.Token); + while (EnableSecondPasses && _secondPasses.Contains(itemId)) + { + _secondPasses.TryRemove(itemId); + await ProcessItem(itemId, _cancellationTokenSource.Token); + } } catch (OperationCanceledException) { @@ -155,8 +143,14 @@ private Task ExecuteItem(string itemId) { Logger.LogError(default(EventId), ex, $"Error executing item {itemId} - {ex.Message}"); } - - return Task.CompletedTask; + finally + { + waitHandle.Set(); + lock (_activeTasks) + { + _activeTasks.Remove(itemId); + } + } } } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index b1e8c48df..d3a447a9f 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.2.4 - 3.2.4.0 - 3.2.4.0 + 3.2.5 + 3.2.5.0 + 3.2.5.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.2.4 + 3.2.5 From b80fc13b0f4e691089476af5a541c19e57fcab5f Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 19 Sep 2020 17:09:06 -0700 Subject: [PATCH 234/462] Cosmos DB support (#649) --- README.md | 1 + docs/persistence.md | 1 + .../Interface/ICosmosDbProvisioner.cs | 9 + .../Models/PersistedEvent.cs | 50 +++++ .../Models/PersistedSubscription.cs | 70 +++++++ .../Models/PersistedWorkflow.cs | 75 +++++++ .../WorkflowCore.Providers.Azure/README.md | 2 + .../ServiceCollectionExtensions.cs | 8 + .../Services/CosmosDbPersistenceProvider.cs | 195 ++++++++++++++++++ .../Services/CosmosDbProvisioner.cs | 38 ++++ .../WorkflowCore.Providers.Azure.csproj | 10 +- 11 files changed, 455 insertions(+), 4 deletions(-) create mode 100644 src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbProvisioner.cs create mode 100644 src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs create mode 100644 src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs create mode 100644 src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs create mode 100644 src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs create mode 100644 src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs diff --git a/README.md b/README.md index 9c55cc475..53ef98486 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ There are several persistence providers available as separate Nuget packages. * MemoryPersistenceProvider *(Default provider, for demo and testing purposes)* * [MongoDB](src/providers/WorkflowCore.Persistence.MongoDB) +* [Cosmos DB](src/providers/WorkflowCore.Providers.Azure) * [Amazon DynamoDB](src/providers/WorkflowCore.Providers.AWS) * [SQL Server](src/providers/WorkflowCore.Persistence.SqlServer) * [PostgreSQL](src/providers/WorkflowCore.Persistence.PostgreSQL) diff --git a/docs/persistence.md b/docs/persistence.md index 81a880c63..8a7fe55fd 100644 --- a/docs/persistence.md +++ b/docs/persistence.md @@ -9,4 +9,5 @@ There are several persistence providers available as separate Nuget packages. * [PostgreSQL](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Persistence.PostgreSQL) * [Sqlite](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Persistence.Sqlite) * [Amazon DynamoDB](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.AWS) +* [Cosmos DB](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.Azure) * [Redis](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Providers.Redis) diff --git a/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbProvisioner.cs b/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbProvisioner.cs new file mode 100644 index 000000000..67a738971 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbProvisioner.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace WorkflowCore.Providers.Azure.Interface +{ + public interface ICosmosDbProvisioner + { + Task Provision(string dbId); + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs new file mode 100644 index 000000000..341f7b71b --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs @@ -0,0 +1,50 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using WorkflowCore.Models; + +namespace WorkflowCore.Providers.Azure.Models +{ + public class PersistedEvent + { + public string id { get; set; } + + public string EventName { get; set; } + + public string EventKey { get; set; } + + public string EventData { get; set; } + + public DateTime EventTime { get; set; } + + public bool IsProcessed { get; set; } + + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + + public static PersistedEvent FromInstance(Event instance) + { + return new PersistedEvent() + { + id = instance.Id, + EventKey = instance.EventKey, + EventName = instance.EventName, + EventTime = instance.EventTime, + IsProcessed = instance.IsProcessed, + EventData = JsonConvert.SerializeObject(instance.EventData, SerializerSettings), + }; + } + + public static Event ToInstance(PersistedEvent instance) + { + return new Event() + { + Id = instance.id, + EventKey = instance.EventKey, + EventName = instance.EventName, + EventTime = instance.EventTime, + IsProcessed = instance.IsProcessed, + EventData = JsonConvert.DeserializeObject(instance.EventData, SerializerSettings), + }; + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs new file mode 100644 index 000000000..d0660046c --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using WorkflowCore.Models; + +namespace WorkflowCore.Providers.Azure.Models +{ + public class PersistedSubscription + { + public string id { get; set; } + + public string WorkflowId { get; set; } + + public int StepId { get; set; } + + public string ExecutionPointerId { get; set; } + + public string EventName { get; set; } + + public string EventKey { get; set; } + + public DateTime SubscribeAsOf { get; set; } + + public string SubscriptionData { get; set; } + + public string ExternalToken { get; set; } + + public string ExternalWorkerId { get; set; } + + public DateTime? ExternalTokenExpiry { get; set; } + + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + + public static PersistedSubscription FromInstance(EventSubscription instance) + { + return new PersistedSubscription() + { + id = instance.Id, + EventKey = instance.EventKey, + EventName = instance.EventName, + ExecutionPointerId = instance.ExecutionPointerId, + ExternalToken = instance.ExternalToken, + ExternalTokenExpiry = instance.ExternalTokenExpiry, + ExternalWorkerId = instance.ExternalWorkerId, + StepId = instance.StepId, + SubscribeAsOf = instance.SubscribeAsOf, + WorkflowId = instance.WorkflowId, + SubscriptionData = JsonConvert.SerializeObject(instance.SubscriptionData, SerializerSettings), + }; + } + + public static EventSubscription ToInstance(PersistedSubscription instance) + { + return new EventSubscription() + { + Id = instance.id, + EventKey = instance.EventKey, + EventName = instance.EventName, + ExecutionPointerId = instance.ExecutionPointerId, + ExternalToken = instance.ExternalToken, + ExternalTokenExpiry = instance.ExternalTokenExpiry, + ExternalWorkerId = instance.ExternalWorkerId, + StepId = instance.StepId, + SubscribeAsOf = instance.SubscribeAsOf, + WorkflowId = instance.WorkflowId, + SubscriptionData = JsonConvert.DeserializeObject(instance.SubscriptionData, SerializerSettings), + }; + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs new file mode 100644 index 000000000..c9f565592 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs @@ -0,0 +1,75 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using WorkflowCore.Models; + +namespace WorkflowCore.Providers.Azure.Models +{ + public class PersistedWorkflow + { + public string id { get; set; } + + public string WorkflowDefinitionId { get; set; } + + public int Version { get; set; } + + public string Description { get; set; } + + public string Reference { get; set; } + + public string ExecutionPointers { get; set; } + + public long? NextExecution { get; set; } + + public WorkflowStatus Status { get; set; } + + public string Data { get; set; } + + public DateTime CreateTime { get; set; } + + public DateTime? CompleteTime { get; set; } + + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + + public static PersistedWorkflow FromInstance(WorkflowInstance instance) + { + var result = new PersistedWorkflow() + { + id = instance.Id, + CompleteTime = instance.CompleteTime, + CreateTime = instance.CreateTime, + Description = instance.Description, + NextExecution = instance.NextExecution, + Reference = instance.Reference, + Status = instance.Status, + Version = instance.Version, + WorkflowDefinitionId = instance.WorkflowDefinitionId, + Data = JsonConvert.SerializeObject(instance.Data, SerializerSettings), + ExecutionPointers = JsonConvert.SerializeObject(instance.ExecutionPointers, SerializerSettings), + }; + + return result; + } + + public static WorkflowInstance ToInstance(PersistedWorkflow instance) + { + var result = new WorkflowInstance() + { + Id = instance.id, + CompleteTime = instance.CompleteTime, + CreateTime = instance.CreateTime, + Description = instance.Description, + NextExecution = instance.NextExecution, + Reference = instance.Reference, + Status = instance.Status, + Version = instance.Version, + WorkflowDefinitionId = instance.WorkflowDefinitionId, + Data = JsonConvert.DeserializeObject(instance.Data, SerializerSettings), + ExecutionPointers = JsonConvert.DeserializeObject(instance.ExecutionPointers, SerializerSettings), + }; + + return result; + } + + } +} diff --git a/src/providers/WorkflowCore.Providers.Azure/README.md b/src/providers/WorkflowCore.Providers.Azure/README.md index a6641c564..10c17bfbe 100644 --- a/src/providers/WorkflowCore.Providers.Azure/README.md +++ b/src/providers/WorkflowCore.Providers.Azure/README.md @@ -3,6 +3,7 @@ * Provides [DLM](https://en.wikipedia.org/wiki/Distributed_lock_manager) support on [Workflow Core](../../README.md) using Azure Blob Storage leases. * Provides Queueing support on [Workflow Core](../../README.md) using Azure Storage queues. * Provides event hub support on [Workflow Core](../../README.md) backed by Azure Service Bus. +* Provides persistence on [Workflow Core](../../README.md) backed by Azure Cosmos DB. This makes it possible to have a cluster of nodes processing your workflows. @@ -30,5 +31,6 @@ services.AddWorkflow(options => { options.UseAzureSynchronization("azure storage connection string"); options.UseAzureServiceBusEventHub("service bus connection string", "topic name", "subscription name"); + options.UseCosmosDbPersistence("connection string"); }); ``` \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs index 5abb111fc..9129e4de5 100644 --- a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Logging; using WorkflowCore.Models; +using WorkflowCore.Providers.Azure.Interface; using WorkflowCore.Providers.Azure.Services; namespace Microsoft.Extensions.DependencyInjection @@ -24,5 +25,12 @@ public static WorkflowOptions UseAzureServiceBusEventHub( return options; } + + public static WorkflowOptions UseCosmosDbPersistence(this WorkflowOptions options, string connectionString, string databaseId) + { + options.Services.AddTransient(sp => new CosmosDbProvisioner(connectionString, sp.GetService())); + options.UsePersistence(sp => new CosmosDbPersistenceProvider(connectionString, databaseId, sp.GetService())); + return options; + } } } diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs new file mode 100644 index 000000000..8ca697fc2 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Azure.Cosmos; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Providers.Azure.Interface; +using WorkflowCore.Providers.Azure.Models; + +namespace WorkflowCore.Providers.Azure.Services +{ + public class CosmosDbPersistenceProvider : IPersistenceProvider + { + + public const string WorkflowContainerName = "workflows"; + public const string EventContainerName = "events"; + public const string SubscriptionContainerName = "subscriptions"; + + private ICosmosDbProvisioner _provisioner; + private string _dbId; + private CosmosClient _client; + private Lazy _workflowContainer; + private Lazy _eventContainer; + private Lazy _subscriptionContainer; + + public CosmosDbPersistenceProvider(string connectionString, string dbId, ICosmosDbProvisioner provisioner) + { + _provisioner = provisioner; + _dbId = dbId; + _client = new CosmosClient(connectionString); + _workflowContainer = new Lazy(() => _client.GetDatabase(_dbId).GetContainer(WorkflowContainerName)); + _eventContainer = new Lazy(() => _client.GetDatabase(_dbId).GetContainer(EventContainerName)); + _subscriptionContainer = new Lazy(() => _client.GetDatabase(_dbId).GetContainer(SubscriptionContainerName)); + } + + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + { + var existing = await _subscriptionContainer.Value.ReadItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId)); + + if (existing.Resource.ExternalToken != token) + throw new InvalidOperationException(); + existing.Resource.ExternalToken = null; + existing.Resource.ExternalWorkerId = null; + existing.Resource.ExternalTokenExpiry = null; + + await _subscriptionContainer.Value.ReplaceItemAsync(existing.Resource, eventSubscriptionId); + } + + public async Task CreateEvent(Event newEvent) + { + newEvent.Id = Guid.NewGuid().ToString(); + var result = await _eventContainer.Value.CreateItemAsync(PersistedEvent.FromInstance(newEvent)); + return result.Resource.id; + } + + public async Task CreateEventSubscription(EventSubscription subscription) + { + subscription.Id = Guid.NewGuid().ToString(); + var result = await _subscriptionContainer.Value.CreateItemAsync(PersistedSubscription.FromInstance(subscription)); + return result.Resource.id; + } + + public async Task CreateNewWorkflow(WorkflowInstance workflow) + { + workflow.Id = Guid.NewGuid().ToString(); + var result = await _workflowContainer.Value.CreateItemAsync(PersistedWorkflow.FromInstance(workflow)); + return result.Resource.id; + } + + public void EnsureStoreExists() + { + _provisioner.Provision(_dbId).Wait(); + } + + public async Task GetEvent(string id) + { + var resp = await _eventContainer.Value.ReadItemAsync(id, new PartitionKey(id)); + return PersistedEvent.ToInstance(resp.Resource); + } + + public Task> GetEvents(string eventName, string eventKey, DateTime asOf) + { + var data = _eventContainer.Value.GetItemLinqQueryable(true) + .Where(x => x.EventName == eventName && x.EventKey == eventKey) + .Where(x => x.EventTime >= asOf) + .Select(x => x.id); + + return Task.FromResult(data.AsEnumerable()); + } + + public Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + { + var data = _subscriptionContainer.Value.GetItemLinqQueryable(true) + .FirstOrDefault(x => x.ExternalToken == null && x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf); + + return Task.FromResult(PersistedSubscription.ToInstance(data)); + } + + public Task> GetRunnableEvents(DateTime asAt) + { + var data = _eventContainer.Value.GetItemLinqQueryable(true) + .Where(x => !x.IsProcessed) + .Where(x => x.EventTime <= asAt.ToUniversalTime()) + .Select(x => x.id); + + return Task.FromResult(data.AsEnumerable()); + } + + public Task> GetRunnableInstances(DateTime asAt) + { + var now = asAt.ToUniversalTime().Ticks; + + var data = _workflowContainer.Value.GetItemLinqQueryable(true) + .Where(x => x.NextExecution.HasValue && (x.NextExecution <= now) && (x.Status == WorkflowStatus.Runnable)) + .Select(x => x.id); + + return Task.FromResult(data.AsEnumerable()); + } + + public async Task GetSubscription(string eventSubscriptionId) + { + var resp = await _subscriptionContainer.Value.ReadItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId)); + return PersistedSubscription.ToInstance(resp.Resource); + } + + public Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) + { + var data = _subscriptionContainer.Value.GetItemLinqQueryable(true) + .Where(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf) + .ToList() + .Select(x => PersistedSubscription.ToInstance(x)); + return Task.FromResult(data.AsEnumerable()); + } + + public async Task GetWorkflowInstance(string Id) + { + var result = await _workflowContainer.Value.ReadItemAsync(Id, new PartitionKey(Id)); + return PersistedWorkflow.ToInstance(result.Resource); + } + + public Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) + { + throw new NotImplementedException(); + } + + public Task> GetWorkflowInstances(IEnumerable ids) + { + throw new NotImplementedException(); + } + + public async Task MarkEventProcessed(string id) + { + var evt = await _eventContainer.Value.ReadItemAsync(id, new PartitionKey(id)); + evt.Resource.IsProcessed = true; + await _eventContainer.Value.ReplaceItemAsync(evt.Resource, id); + } + + public async Task MarkEventUnprocessed(string id) + { + var evt = await _eventContainer.Value.ReadItemAsync(id, new PartitionKey(id)); + evt.Resource.IsProcessed = false; + await _eventContainer.Value.ReplaceItemAsync(evt.Resource, id); + } + + public Task PersistErrors(IEnumerable errors) + { + return Task.CompletedTask; + } + + public async Task PersistWorkflow(WorkflowInstance workflow) + { + await _workflowContainer.Value.UpsertItemAsync(PersistedWorkflow.FromInstance(workflow)); + } + + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + { + var sub = await _subscriptionContainer.Value.ReadItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId)); + var existingEntity = sub.Resource; + existingEntity.ExternalToken = token; + existingEntity.ExternalWorkerId = workerId; + existingEntity.ExternalTokenExpiry = expiry; + + await _subscriptionContainer.Value.ReplaceItemAsync(existingEntity, eventSubscriptionId); + + return true; + } + + public async Task TerminateSubscription(string eventSubscriptionId) + { + await _subscriptionContainer.Value.DeleteItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId)); + } + } +} diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs new file mode 100644 index 000000000..001ade0a0 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Microsoft.Azure.Cosmos; +using Microsoft.Extensions.Logging; +using WorkflowCore.Providers.Azure.Interface; + +namespace WorkflowCore.Providers.Azure.Services +{ + public class CosmosDbProvisioner : ICosmosDbProvisioner + { + + private CosmosClient _client; + + public CosmosDbProvisioner(string connectionString, ILoggerFactory loggerFactory) + { + _client = new CosmosClient(connectionString); + } + + public async Task Provision(string dbId) + { + var dbResp = await _client.CreateDatabaseIfNotExistsAsync(dbId); + var wfIndexPolicy = new IndexingPolicy(); + wfIndexPolicy.IncludedPaths.Add(new IncludedPath() { Path = @"/*" }); + wfIndexPolicy.ExcludedPaths.Add(new ExcludedPath() { Path = @"/ExecutionPointers/?" }); + + Task.WaitAll( + dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(CosmosDbPersistenceProvider.WorkflowContainerName, @"/id") + { + IndexingPolicy = wfIndexPolicy + }), + dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(CosmosDbPersistenceProvider.EventContainerName, @"/id")), + dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(CosmosDbPersistenceProvider.SubscriptionContainerName, @"/id")) + ); + } + + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj index 28a8e06f4..43013c80c 100644 --- a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj +++ b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj @@ -7,19 +7,21 @@ - Provides distributed lock management on Workflow Core - Provides Queueing support on Workflow Core workflow workflowcore dlm - 2.0.0 + 3.0.0 $(PackageTargetFallback);dnxcore50 https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git Daniel Gerlag - 2.0.0.0 - 2.0.0.0 + 3.0.0.0 + 3.0.0.0 - + + + From 090334da581f20c71cc1a964707f86e1b099fd99 Mon Sep 17 00:00:00 2001 From: Pavlo Korobov Date: Fri, 25 Sep 2020 13:39:50 +0200 Subject: [PATCH 235/462] RabbitMQProvider.cs improvements --- .../Interfaces/IRabbitMqQueueNameProvider.cs | 11 ++++++ .../ServiceCollectionExtensions.cs | 38 ++++++++++++++++++- .../DefaultRabbitMqQueueNameProvider.cs | 23 +++++++++++ .../Services/RabbitMQProvider.cs | 37 ++++++++---------- 4 files changed, 85 insertions(+), 24 deletions(-) create mode 100644 src/providers/WorkflowCore.QueueProviders.RabbitMQ/Interfaces/IRabbitMqQueueNameProvider.cs create mode 100644 src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/DefaultRabbitMqQueueNameProvider.cs diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Interfaces/IRabbitMqQueueNameProvider.cs b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Interfaces/IRabbitMqQueueNameProvider.cs new file mode 100644 index 000000000..f39d75b52 --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Interfaces/IRabbitMqQueueNameProvider.cs @@ -0,0 +1,11 @@ +using System; +using System.Linq; +using WorkflowCore.Interface; + +namespace WorkflowCore.QueueProviders.RabbitMQ.Interfaces +{ + public interface IRabbitMqQueueNameProvider + { + string GetQueueName(QueueType queue); + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/ServiceCollectionExtensions.cs index b148a0044..e3f13bdfb 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/ServiceCollectionExtensions.cs @@ -2,18 +2,52 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection.Extensions; +using WorkflowCore.Interface; using WorkflowCore.Models; +using WorkflowCore.QueueProviders.RabbitMQ.Interfaces; using WorkflowCore.QueueProviders.RabbitMQ.Services; namespace Microsoft.Extensions.DependencyInjection { + public delegate IConnection RabbitMqConnectionFactory(IServiceProvider sp, string clientProvidedName); + public static class ServiceCollectionExtensions { public static WorkflowOptions UseRabbitMQ(this WorkflowOptions options, IConnectionFactory connectionFactory) { - options.UseQueueProvider(sp => new RabbitMQProvider(connectionFactory)); + if (options == null) throw new ArgumentNullException(nameof(options)); + if (connectionFactory == null) throw new ArgumentNullException(nameof(connectionFactory)); + + return options + .UseRabbitMQ((sp, name) => connectionFactory.CreateConnection(name)); + } + + public static WorkflowOptions UseRabbitMQ(this WorkflowOptions options, + IConnectionFactory connectionFactory, + IEnumerable hostnames) + { + if (options == null) throw new ArgumentNullException(nameof(options)); + if (connectionFactory == null) throw new ArgumentNullException(nameof(connectionFactory)); + if (hostnames == null) throw new ArgumentNullException(nameof(hostnames)); + + return options + .UseRabbitMQ((sp, name) => connectionFactory.CreateConnection(hostnames.ToList(), name)); + } + + public static WorkflowOptions UseRabbitMQ(this WorkflowOptions options, RabbitMqConnectionFactory rabbitMqConnectionFactory) + { + if (options == null) throw new ArgumentNullException(nameof(options)); + if (rabbitMqConnectionFactory == null) throw new ArgumentNullException(nameof(rabbitMqConnectionFactory)); + + options.Services.AddSingleton(rabbitMqConnectionFactory); + options.Services.TryAddTransient(); + options.UseQueueProvider(RabbitMqQueueProviderFactory); + return options; } + + private static IQueueProvider RabbitMqQueueProviderFactory(IServiceProvider sp) + => new RabbitMQProvider(sp); } } diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/DefaultRabbitMqQueueNameProvider.cs b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/DefaultRabbitMqQueueNameProvider.cs new file mode 100644 index 000000000..120de752e --- /dev/null +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/DefaultRabbitMqQueueNameProvider.cs @@ -0,0 +1,23 @@ +using WorkflowCore.Interface; +using WorkflowCore.QueueProviders.RabbitMQ.Interfaces; + +namespace WorkflowCore.QueueProviders.RabbitMQ.Services +{ + public class DefaultRabbitMqQueueNameProvider : IRabbitMqQueueNameProvider + { + public string GetQueueName(QueueType queue) + { + switch (queue) + { + case QueueType.Workflow: + return "wfc.workflow_queue"; + case QueueType.Event: + return "wfc.event_queue"; + case QueueType.Index: + return "wfc.index_queue"; + default: + return null; + } + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs index c28091092..0dd1c1490 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs @@ -7,23 +7,30 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Models; +using WorkflowCore.QueueProviders.RabbitMQ.Interfaces; namespace WorkflowCore.QueueProviders.RabbitMQ.Services { #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously public class RabbitMQProvider : IQueueProvider { - private readonly IConnectionFactory _connectionFactory; + private readonly IRabbitMqQueueNameProvider _queueNameProvider; + private readonly RabbitMqConnectionFactory _rabbitMqConnectionFactory; + private readonly IServiceProvider _serviceProvider; + private IConnection _connection = null; private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; public bool IsDequeueBlocking => false; - public RabbitMQProvider(IConnectionFactory connectionFactory) + public RabbitMQProvider(IServiceProvider serviceProvider) { - _connectionFactory = connectionFactory; + _serviceProvider = serviceProvider; + _queueNameProvider = _serviceProvider.GetRequiredService(); + _rabbitMqConnectionFactory = _serviceProvider.GetRequiredService(); } public async Task QueueWork(string id, QueueType queue) @@ -33,9 +40,9 @@ public async Task QueueWork(string id, QueueType queue) using (var channel = _connection.CreateModel()) { - channel.QueueDeclare(queue: GetQueueName(queue), durable: true, exclusive: false, autoDelete: false, arguments: null); + channel.QueueDeclare(queue: _queueNameProvider.GetQueueName(queue), durable: true, exclusive: false, autoDelete: false, arguments: null); var body = Encoding.UTF8.GetBytes(id); - channel.BasicPublish(exchange: "", routingKey: GetQueueName(queue), basicProperties: null, body: body); + channel.BasicPublish(exchange: "", routingKey: _queueNameProvider.GetQueueName(queue), basicProperties: null, body: body); } } @@ -46,7 +53,7 @@ public async Task DequeueWork(QueueType queue, CancellationToken cancell using (var channel = _connection.CreateModel()) { - channel.QueueDeclare(queue: GetQueueName(queue), + channel.QueueDeclare(queue: _queueNameProvider.GetQueueName(queue), durable: true, exclusive: false, autoDelete: false, @@ -54,7 +61,7 @@ public async Task DequeueWork(QueueType queue, CancellationToken cancell channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false); - var msg = channel.BasicGet(GetQueueName(queue), false); + var msg = channel.BasicGet(_queueNameProvider.GetQueueName(queue), false); if (msg != null) { var data = Encoding.UTF8.GetString(msg.Body); @@ -76,7 +83,7 @@ public void Dispose() public async Task Start() { - _connection = _connectionFactory.CreateConnection("Workflow-Core"); + _connection = _rabbitMqConnectionFactory(_serviceProvider, "Workflow-Core"); } public async Task Stop() @@ -88,20 +95,6 @@ public async Task Stop() } } - private string GetQueueName(QueueType queue) - { - switch (queue) - { - case QueueType.Workflow: - return "wfc.workflow_queue"; - case QueueType.Event: - return "wfc.event_queue"; - case QueueType.Index: - return "wfc.index_queue"; - } - return null; - } - } #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously } From e760e181a6c4394464c4378d2d46f38381399a3c Mon Sep 17 00:00:00 2001 From: Pavlo Korobov Date: Fri, 25 Sep 2020 13:53:25 +0200 Subject: [PATCH 236/462] QueueNameProvider tests --- WorkflowCore.sln | 7 +++++ .../DefaultRabbitMqQueueNameProviderTests.cs | 28 +++++++++++++++++++ ...wCore.Tests.QueueProviders.RabbitMQ.csproj | 21 ++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 test/WorkflowCore.Tests.QueueProviders.RabbitMQ/Tests/DefaultRabbitMqQueueNameProviderTests.cs create mode 100644 test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 5134c23dc..9231faa76 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -145,6 +145,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample09s", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchPad\ScratchPad.csproj", "{51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.QueueProviders.RabbitMQ", "test\WorkflowCore.Tests.QueueProviders.RabbitMQ\WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj", "{54DE20BA-EBA7-4BF0-9BD9-F03766849716}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -355,6 +357,10 @@ Global {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C}.Debug|Any CPU.Build.0 = Debug|Any CPU {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C}.Release|Any CPU.ActiveCfg = Release|Any CPU {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C}.Release|Any CPU.Build.0 = Release|Any CPU + {54DE20BA-EBA7-4BF0-9BD9-F03766849716}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54DE20BA-EBA7-4BF0-9BD9-F03766849716}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54DE20BA-EBA7-4BF0-9BD9-F03766849716}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54DE20BA-EBA7-4BF0-9BD9-F03766849716}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -414,6 +420,7 @@ Global {5BE6D628-B9DB-4C76-AAEB-8F3800509A84} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {E32CF21A-29CC-46D1-8044-FCC327F2B281} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} + {54DE20BA-EBA7-4BF0-9BD9-F03766849716} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/Tests/DefaultRabbitMqQueueNameProviderTests.cs b/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/Tests/DefaultRabbitMqQueueNameProviderTests.cs new file mode 100644 index 000000000..66bd39233 --- /dev/null +++ b/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/Tests/DefaultRabbitMqQueueNameProviderTests.cs @@ -0,0 +1,28 @@ +using FluentAssertions; +using WorkflowCore.Interface; +using WorkflowCore.QueueProviders.RabbitMQ.Services; +using Xunit; + +namespace WorkflowCore.Tests.QueueProviders.RabbitMQ.Tests +{ + public class DefaultRabbitMqQueueNameProviderTests + { + private readonly DefaultRabbitMqQueueNameProvider _sut; + + public DefaultRabbitMqQueueNameProviderTests() + { + _sut = new DefaultRabbitMqQueueNameProvider(); + } + + [Theory] + [InlineData(QueueType.Event, "wfc.event_queue")] + [InlineData(QueueType.Index, "wfc.index_queue")] + [InlineData(QueueType.Workflow, "wfc.workflow_queue")] + public void GetQueueName_ValidInput_ReturnsValidQueueName(QueueType queueType, string queueName) + { + var result = _sut.GetQueueName(queueType); + + result.Should().Be(queueName); + } + } +} \ No newline at end of file diff --git a/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj b/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj new file mode 100644 index 000000000..7ccd35c02 --- /dev/null +++ b/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + From d56579abbcebf30a6083a2623b02fc7bda9e72df Mon Sep 17 00:00:00 2001 From: Pavlo Korobov Date: Mon, 5 Oct 2020 13:41:58 +0200 Subject: [PATCH 237/462] Fix suggestions --- .../ServiceCollectionExtensions.cs | 6 ++++-- .../Services/RabbitMQProvider.cs | 8 +++++--- .../WorkflowCore.QueueProviders.RabbitMQ.csproj | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/ServiceCollectionExtensions.cs index e3f13bdfb..4474e403a 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/ServiceCollectionExtensions.cs @@ -41,13 +41,15 @@ public static WorkflowOptions UseRabbitMQ(this WorkflowOptions options, RabbitMq if (rabbitMqConnectionFactory == null) throw new ArgumentNullException(nameof(rabbitMqConnectionFactory)); options.Services.AddSingleton(rabbitMqConnectionFactory); - options.Services.TryAddTransient(); + options.Services.TryAddSingleton(); options.UseQueueProvider(RabbitMqQueueProviderFactory); return options; } private static IQueueProvider RabbitMqQueueProviderFactory(IServiceProvider sp) - => new RabbitMQProvider(sp); + => new RabbitMQProvider(sp, + sp.GetRequiredService(), + sp.GetRequiredService()); } } diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs index 0dd1c1490..d9ec3bbeb 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs @@ -26,11 +26,13 @@ public class RabbitMQProvider : IQueueProvider public bool IsDequeueBlocking => false; - public RabbitMQProvider(IServiceProvider serviceProvider) + public RabbitMQProvider(IServiceProvider serviceProvider, + IRabbitMqQueueNameProvider queueNameProvider, + RabbitMqConnectionFactory connectionFactory) { _serviceProvider = serviceProvider; - _queueNameProvider = _serviceProvider.GetRequiredService(); - _rabbitMqConnectionFactory = _serviceProvider.GetRequiredService(); + _queueNameProvider = queueNameProvider; + _rabbitMqConnectionFactory = connectionFactory; } public async Task QueueWork(string id, QueueType queue) diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj index 6735f21eb..025e83913 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj @@ -19,6 +19,7 @@ Queue provider for Workflow-core using RabbitMQ 2.0.0.0 2.0.0.0 + 2.1.0 From e11f6962a1ab6616d5f48ecce1a650187331473e Mon Sep 17 00:00:00 2001 From: ioanadumitru Date: Tue, 6 Oct 2020 18:00:42 +0300 Subject: [PATCH 238/462] Update WorkflowRegistry.cs Changed registry collection to a thread safe collection. --- src/WorkflowCore/Services/WorkflowRegistry.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowRegistry.cs b/src/WorkflowCore/Services/WorkflowRegistry.cs index 5677d434d..c09189a1c 100644 --- a/src/WorkflowCore/Services/WorkflowRegistry.cs +++ b/src/WorkflowCore/Services/WorkflowRegistry.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; @@ -10,7 +11,7 @@ namespace WorkflowCore.Services public class WorkflowRegistry : IWorkflowRegistry { private readonly IServiceProvider _serviceProvider; - private readonly List> _registry = new List>(); + private readonly BlockingCollection> _registry = new BlockingCollection>(); public WorkflowRegistry(IServiceProvider serviceProvider) { @@ -35,10 +36,10 @@ public WorkflowDefinition GetDefinition(string workflowId, int? version = null) public void DeregisterWorkflow(string workflowId, int version) { - var definition = _registry.Find(x => x.Item1 == workflowId && x.Item2 == version); + var definition = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version); if (definition != null) { - _registry.Remove(definition); + _registry.TryTake(out definition); } } @@ -81,7 +82,7 @@ public void RegisterWorkflow(IWorkflow workflow) public bool IsRegistered(string workflowId, int version) { - var definition = _registry.Find(x => x.Item1 == workflowId && x.Item2 == version); + var definition = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version); return (definition != null); } From 4b3422e14315acb303236b22c32cee211db1cc8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=88=91=E9=9B=B60=E4=B8=83?= <007@wangshuai.app> Date: Tue, 20 Oct 2020 11:03:13 +0800 Subject: [PATCH 239/462] add support for WorkflowStep Before, the `StepType` field must be subclass of StepBody Now,It support for `WorkflowStep` Old: public class HelloWorld : StepBody new:(Add) public class EndStep : WorkflowStep --- .../Services/DefinitionLoader.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index 810d6338b..27d84492b 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -68,8 +68,22 @@ private WorkflowStepCollection ConvertSteps(ICollection source, Ty var nextStep = stack.Pop(); var stepType = FindType(nextStep.StepType); - var containerType = typeof(WorkflowStep<>).MakeGenericType(stepType); - var targetStep = (containerType.GetConstructor(new Type[] { }).Invoke(null) as WorkflowStep); + + WorkflowStep targetStep; + + Type containerType; + if (stepType.IsSubclassOf(typeof(StepBody))) + { + containerType = typeof(WorkflowStep<>).MakeGenericType(stepType); + + targetStep = (containerType.GetConstructor(new Type[] { }).Invoke(null) as WorkflowStep); + } + else + { + targetStep = stepType.GetConstructor(new Type[] { }).Invoke(null) as WorkflowStep; + if (targetStep != null) + stepType = targetStep.BodyType; + } if (nextStep.Saga) { From 665e945d94da566acac6e6b9fffccd1da6295d4f Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 21 Oct 2020 17:37:15 -0700 Subject: [PATCH 240/462] pass current context to default branch --- src/WorkflowCore/Primitives/If.cs | 2 +- src/WorkflowCore/Primitives/OutcomeSwitch.cs | 2 +- src/WorkflowCore/Primitives/Schedule.cs | 2 +- src/WorkflowCore/Primitives/Sequence.cs | 2 +- src/WorkflowCore/Primitives/When.cs | 2 +- src/WorkflowCore/Primitives/While.cs | 2 +- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- src/extensions/WorkflowCore.Users/Primitives/UserTask.cs | 2 +- .../WorkflowCore.Users/WorkflowCore.Users.csproj | 6 +++--- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/WorkflowCore/Primitives/If.cs b/src/WorkflowCore/Primitives/If.cs index 2aaaad963..1f7085788 100644 --- a/src/WorkflowCore/Primitives/If.cs +++ b/src/WorkflowCore/Primitives/If.cs @@ -15,7 +15,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (Condition) { - return ExecutionResult.Branch(new List() { null }, new ControlPersistenceData() { ChildrenActive = true }); + return ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); } return ExecutionResult.Next(); diff --git a/src/WorkflowCore/Primitives/OutcomeSwitch.cs b/src/WorkflowCore/Primitives/OutcomeSwitch.cs index 74c2cf1de..d2dcc1e79 100644 --- a/src/WorkflowCore/Primitives/OutcomeSwitch.cs +++ b/src/WorkflowCore/Primitives/OutcomeSwitch.cs @@ -12,7 +12,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (context.PersistenceData == null) { - var result = ExecutionResult.Branch(new List() { null }, new ControlPersistenceData() { ChildrenActive = true }); + var result = ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); result.OutcomeValue = GetPreviousOutcome(context); return result; } diff --git a/src/WorkflowCore/Primitives/Schedule.cs b/src/WorkflowCore/Primitives/Schedule.cs index 9c74f2a97..e279cb232 100644 --- a/src/WorkflowCore/Primitives/Schedule.cs +++ b/src/WorkflowCore/Primitives/Schedule.cs @@ -20,7 +20,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (!((SchedulePersistenceData)context.PersistenceData).Elapsed) { - return ExecutionResult.Branch(new List() { null }, new SchedulePersistenceData() { Elapsed = true }); + return ExecutionResult.Branch(new List() { context.Item }, new SchedulePersistenceData() { Elapsed = true }); } if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) diff --git a/src/WorkflowCore/Primitives/Sequence.cs b/src/WorkflowCore/Primitives/Sequence.cs index 20140af47..d31206a79 100644 --- a/src/WorkflowCore/Primitives/Sequence.cs +++ b/src/WorkflowCore/Primitives/Sequence.cs @@ -11,7 +11,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (context.PersistenceData == null) { - return ExecutionResult.Branch(new List() { null }, new ControlPersistenceData() { ChildrenActive = true }); + return ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); } if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) diff --git a/src/WorkflowCore/Primitives/When.cs b/src/WorkflowCore/Primitives/When.cs index ca9434f66..f5f292ad1 100644 --- a/src/WorkflowCore/Primitives/When.cs +++ b/src/WorkflowCore/Primitives/When.cs @@ -25,7 +25,7 @@ public override ExecutionResult Run(IStepExecutionContext context) if (context.PersistenceData == null) { - return ExecutionResult.Branch(new List() { null }, new ControlPersistenceData() { ChildrenActive = true }); + return ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); } if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) diff --git a/src/WorkflowCore/Primitives/While.cs b/src/WorkflowCore/Primitives/While.cs index 154d7cf87..ba5abc2e6 100644 --- a/src/WorkflowCore/Primitives/While.cs +++ b/src/WorkflowCore/Primitives/While.cs @@ -15,7 +15,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (Condition) { - return ExecutionResult.Branch(new List() { null }, new ControlPersistenceData() { ChildrenActive = true }); + return ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); } return ExecutionResult.Next(); diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index d3a447a9f..c4ff1258a 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.2.5 - 3.2.5.0 - 3.2.5.0 + 3.2.6 + 3.2.6.0 + 3.2.6.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.2.5 + 3.2.6 diff --git a/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs b/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs index 5f67ca443..a1edd9b72 100644 --- a/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs +++ b/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs @@ -50,7 +50,7 @@ public override ExecutionResult Run(IStepExecutionContext context) if (context.PersistenceData == null) { - var result = ExecutionResult.Branch(new List() { null }, new ControlPersistenceData() { ChildrenActive = true }); + var result = ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); result.OutcomeValue = action.OutcomeValue; return result; } diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 742f848f5..5a8c0beab 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -15,9 +15,9 @@ false false Provides extensions for Workflow Core to enable human workflows. - 2.1.0 - 2.1.0.0 - 2.1.0.0 + 2.1.1 + 2.1.1.0 + 2.1.1.0 From 2aaf7bddef4eac9c0f3f6ad205b9090c3ec40d21 Mon Sep 17 00:00:00 2001 From: DanilF Date: Mon, 19 Oct 2020 16:18:20 -0400 Subject: [PATCH 241/462] Add middleware runner, step executor, and ability to run middleware around workflow steps and before/after workflow Add sample with a sample middleware for retrying and log correlation as well as workflow pre/post samples Add async overloads for StartWorkflow and WaitForWorkflowToComplete in integration tests Add error handling of post workflow middleware Add docs for workflow middleware --- WorkflowCore.sln | 7 + docs/samples.md | 4 +- docs/workflow-middleware.md | 279 ++++++++++++++++++ mkdocs.yml | 1 + .../Models/v1/DefinitionSourceV1.cs | 1 - .../Services/DefinitionLoader.cs | 4 +- src/WorkflowCore/Interface/IStepExecutor.cs | 22 ++ .../Interface/IWorkflowBuilder.cs | 7 +- .../Interface/IWorkflowMiddleware.cs | 41 +++ .../IWorkflowMiddlewareErrorHandler.cs | 18 ++ .../Interface/IWorkflowMiddlewareRunner.cs | 33 +++ .../Interface/IWorkflowStepMiddleware.cs | 28 ++ src/WorkflowCore/Models/WorkflowDefinition.cs | 12 +- src/WorkflowCore/Models/WorkflowDelegate.cs | 9 + .../Models/WorkflowStepDelegate.cs | 9 + .../ServiceCollectionExtensions.cs | 39 ++- .../DefaultWorkflowMiddlewareErrorHandler.cs | 32 ++ .../FluentBuilders/WorkflowBuilder.cs | 2 +- src/WorkflowCore/Services/StepExecutor.cs | 52 ++++ .../Services/WorkflowController.cs | 10 +- src/WorkflowCore/Services/WorkflowExecutor.cs | 34 ++- .../Services/WorkflowMiddlewareRunner.cs | 96 ++++++ .../FlakyConnectionParams.cs | 9 + .../FlakyConnectionWorkflow.cs | 25 ++ .../IDescriptiveWorkflowParams.cs | 7 + .../AddDescriptionWorkflowMiddleware.cs | 20 ++ .../LogCorrelationStepMiddleware.cs | 38 +++ .../Middleware/PollyRetryMiddleware.cs | 63 ++++ .../PrintWorkflowSummaryMiddleware.cs | 41 +++ src/samples/WorkflowCore.Sample19/Program.cs | 66 +++++ .../Steps/FlakyConnection.cs | 27 ++ .../WorkflowCore.Sample19/Steps/LogMessage.cs | 29 ++ .../WorkflowCore.Sample19.csproj | 20 ++ .../Scenarios/MiddlewareScenario.cs | 167 +++++++++++ .../Scenarios/StoredJsonScenario.cs | 3 +- .../Scenarios/StoredYamlScenario.cs | 3 +- .../DistributedLockProviderTests.cs | 11 +- .../stored-definition.json | 2 +- test/WorkflowCore.Testing/JsonWorkflowTest.cs | 4 +- test/WorkflowCore.Testing/WorkflowTest.cs | 24 +- test/WorkflowCore.Testing/YamlWorkflowTest.cs | 3 +- .../Services/StepExecutorTests.cs | 148 ++++++++++ .../Services/WorkflowExecutorFixture.cs | 45 ++- .../Services/WorkflowMiddlewareRunnerTests.cs | 276 +++++++++++++++++ 44 files changed, 1713 insertions(+), 58 deletions(-) create mode 100644 docs/workflow-middleware.md create mode 100644 src/WorkflowCore/Interface/IStepExecutor.cs create mode 100644 src/WorkflowCore/Interface/IWorkflowMiddleware.cs create mode 100644 src/WorkflowCore/Interface/IWorkflowMiddlewareErrorHandler.cs create mode 100644 src/WorkflowCore/Interface/IWorkflowMiddlewareRunner.cs create mode 100644 src/WorkflowCore/Interface/IWorkflowStepMiddleware.cs create mode 100644 src/WorkflowCore/Models/WorkflowDelegate.cs create mode 100644 src/WorkflowCore/Models/WorkflowStepDelegate.cs create mode 100644 src/WorkflowCore/Services/DefaultWorkflowMiddlewareErrorHandler.cs create mode 100644 src/WorkflowCore/Services/StepExecutor.cs create mode 100644 src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs create mode 100644 src/samples/WorkflowCore.Sample19/FlakyConnectionParams.cs create mode 100644 src/samples/WorkflowCore.Sample19/FlakyConnectionWorkflow.cs create mode 100644 src/samples/WorkflowCore.Sample19/IDescriptiveWorkflowParams.cs create mode 100644 src/samples/WorkflowCore.Sample19/Middleware/AddDescriptionWorkflowMiddleware.cs create mode 100644 src/samples/WorkflowCore.Sample19/Middleware/LogCorrelationStepMiddleware.cs create mode 100644 src/samples/WorkflowCore.Sample19/Middleware/PollyRetryMiddleware.cs create mode 100644 src/samples/WorkflowCore.Sample19/Middleware/PrintWorkflowSummaryMiddleware.cs create mode 100644 src/samples/WorkflowCore.Sample19/Program.cs create mode 100644 src/samples/WorkflowCore.Sample19/Steps/FlakyConnection.cs create mode 100644 src/samples/WorkflowCore.Sample19/Steps/LogMessage.cs create mode 100644 src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/MiddlewareScenario.cs create mode 100644 test/WorkflowCore.UnitTests/Services/StepExecutorTests.cs create mode 100644 test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 9231faa76..09cda3de8 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -147,6 +147,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchP EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.QueueProviders.RabbitMQ", "test\WorkflowCore.Tests.QueueProviders.RabbitMQ\WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj", "{54DE20BA-EBA7-4BF0-9BD9-F03766849716}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample19", "src\samples\WorkflowCore.Sample19\WorkflowCore.Sample19.csproj", "{1223ED47-3E5E-4960-B70D-DFAF550F6666}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -361,6 +363,10 @@ Global {54DE20BA-EBA7-4BF0-9BD9-F03766849716}.Debug|Any CPU.Build.0 = Debug|Any CPU {54DE20BA-EBA7-4BF0-9BD9-F03766849716}.Release|Any CPU.ActiveCfg = Release|Any CPU {54DE20BA-EBA7-4BF0-9BD9-F03766849716}.Release|Any CPU.Build.0 = Release|Any CPU + {1223ED47-3E5E-4960-B70D-DFAF550F6666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1223ED47-3E5E-4960-B70D-DFAF550F6666}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1223ED47-3E5E-4960-B70D-DFAF550F6666}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1223ED47-3E5E-4960-B70D-DFAF550F6666}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -421,6 +427,7 @@ Global {E32CF21A-29CC-46D1-8044-FCC327F2B281} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {54DE20BA-EBA7-4BF0-9BD9-F03766849716} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} + {1223ED47-3E5E-4960-B70D-DFAF550F6666} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/docs/samples.md b/docs/samples.md index a1b862799..f69290c57 100644 --- a/docs/samples.md +++ b/docs/samples.md @@ -32,4 +32,6 @@ [Exposing a REST API](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WebApiSample) -[Human(User) Workflow](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample08) \ No newline at end of file +[Human(User) Workflow](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample08) + +[Workflow Middleware](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample19) diff --git a/docs/workflow-middleware.md b/docs/workflow-middleware.md new file mode 100644 index 000000000..488d0c7ad --- /dev/null +++ b/docs/workflow-middleware.md @@ -0,0 +1,279 @@ +# Workflow Middleware + +Workflows can be extended with Middleware that run before/after workflows start/complete as well as around workflow steps to provide flexibility in implementing cross-cutting concerns such as [log correlation](https://www.frakkingsweet.com/net-core-log-correlation-easy-access-to-headers/), [retries](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-http-call-retries-exponential-backoff-polly), and other use-cases. + +This is done by implementing and registering `IWorkflowMiddleware` for workflows or `IWorkflowStepMiddleware` for steps. + +## Step Middleware + +Step middleware lets you run additional code around the execution of a given step and alter its behavior. Implementing a step middleware should look familiar to anyone familiar with [ASP.NET Core's middleware pipeline](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1) or [`HttpClient`'s `DelegatingHandler` middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1#outgoing-request-middleware). + +### Usage + +First, create your own middleware class that implements `IWorkflowStepMiddleware`. Here's an example of a middleware that adds workflow ID and step ID to the log correlation context of every workflow step in your app. + +**Important:** You must make sure to call `next()` as part of your middleware. If you do not do this, your step will never run. + +```cs +public class LogCorrelationStepMiddleware : IWorkflowStepMiddleware +{ + private readonly ILogger _log; + + public LogCorrelationStepMiddleware( + ILogger log) + { + _log = log; + } + + public async Task HandleAsync( + IStepExecutionContext context, + IStepBody body, + WorkflowStepDelegate next) + { + var workflowId = context.Workflow.Id; + var stepId = context.Step.Id; + + // Uses log scope to add a few attributes to the scope + using (_log.BeginScope("{@WorkflowId}", workflowId)) + using (_log.BeginScope("{@StepId}", stepId)) + { + // Calling next ensures step gets executed + return await next(); + } + } +} +``` + +Here's another example of a middleware that uses the [Polly](https://github.com/App-vNext/Polly) dotnet resiliency library to implement retries on workflow steps based off a custom retry policy. + +```cs +public class PollyRetryStepMiddleware : IWorkflowStepMiddleware +{ + private const string StepContextKey = "WorkflowStepContext"; + private const int MaxRetries = 3; + private readonly ILogger _log; + + public PollyRetryMiddleware(ILogger log) + { + _log = log; + } + + // Consult Polly's docs for more information on how to build + // retry policies: + // https://github.com/App-vNext/Polly + public IAsyncPolicy GetRetryPolicy() => + Policy + .Handle() + .RetryAsync( + MaxRetries, + (result, retryCount, context) => + UpdateRetryCount( + result.Exception, + retryCount, + context[StepContextKey] as IStepExecutionContext) + ); + + public async Task HandleAsync( + IStepExecutionContext context, + IStepBody body, + WorkflowStepDelegate next + ) + { + return await GetRetryPolicy().ExecuteAsync( + ctx => next(), + // The step execution context gets passed down so that + // the step is accessible within the retry policy + new Dictionary + { + { StepContextKey, context } + }); + } + + private Task UpdateRetryCount( + Exception exception, + int retryCount, + IStepExecutionContext stepContext) + { + var stepInstance = stepContext.ExecutionPointer; + stepInstance.RetryCount = retryCount; + return Task.CompletedTask; + } +} +``` + +## Pre/Post Workflow Middleware + +Workflow middleware run either before a workflow starts or after a workflow completes and can be used to hook into the workflow lifecycle or alter the workflow itself before it is started. + +### Pre Workflow Middleware + +These middleware get run before the workflow is started and can potentially alter properties on the `WorkflowInstance`. + +The following example illustrates setting the `Description` property on the `WorkflowInstance` using a middleware that interprets the data on the passed workflow. This is useful in cases where you want the description of the workflow to be derived from the data passed to the workflow. + +Note that you use `WorkflowMiddlewarePhase.PreWorkflow` to specify that it runs before the workflow starts. + +**Important:** You should call `next` as part of the workflow middleware to ensure that the next workflow in the chain runs. + +```cs +// AddDescriptionWorkflowMiddleware.cs +public class AddDescriptionWorkflowMiddleware : IWorkflowMiddleware +{ + public WorkflowMiddlewarePhase Phase => + WorkflowMiddlewarePhase.PreWorkflow; + + public Task HandleAsync( + WorkflowInstance workflow, + WorkflowDelegate next + ) + { + if (workflow.Data is IDescriptiveWorkflowParams descriptiveParams) + { + workflow.Description = descriptiveParams.Description; + } + + return next(); + } +} + +// IDescriptiveWorkflowParams.cs +public interface IDescriptiveWorkflowParams +{ + string Description { get; } +} + +// MyWorkflowParams.cs +public MyWorkflowParams : IDescriptiveWorkflowParams +{ + public string Description => $"Run task '{TaskName}'"; + + public string TaskName { get; set; } +} +``` + +### Exception Handling in Pre Workflow Middleware + +Pre workflow middleware exception handling gets treated differently from post workflow middleware. Since the middleware runs before the workflow starts, any exceptions thrown within a pre workflow middleware will bubble up to the `StartWorkflow` method and it is up to the caller of `StartWorkflow` to handle the exception and act accordingly. + +```cs +public async Task MyMethodThatStartsAWorkflow() +{ + try + { + await host.StartWorkflow("HelloWorld", 1, null); + } + catch(Exception ex) + { + // Handle the exception appropriately + } +} +``` + +### Post Workflow Middleware + +These middleware get run after the workflow has completed and can be used to perform additional actions for all workflows in your app. + +The following example illustrates how you can use a post workflow middleware to print a summary of the workflow to console. + +Note that you use `WorkflowMiddlewarePhase.PostWorkflow` to specify that it runs after the workflow completes. + +**Important:** You should call `next` as part of the workflow middleware to ensure that the next workflow in the chain runs. + +```cs +public class PrintWorkflowSummaryMiddleware : IWorkflowMiddleware +{ + private readonly ILogger _log; + + public PrintWorkflowSummaryMiddleware( + ILogger log + ) + { + _log = log; + } + + public WorkflowMiddlewarePhase Phase => + WorkflowMiddlewarePhase.PostWorkflow; + + public Task HandleAsync( + WorkflowInstance workflow, + WorkflowDelegate next + ) + { + if (!workflow.CompleteTime.HasValue) + { + return next(); + } + + var duration = workflow.CompleteTime.Value - workflow.CreateTime; + _log.LogInformation($@"Workflow {workflow.Description} completed in {duration:g}"); + + foreach (var step in workflow.ExecutionPointers) + { + var stepName = step.StepName; + var stepDuration = (step.EndTime - step.StartTime) ?? TimeSpan.Zero; + _log.LogInformation($" - Step {stepName} completed in {stepDuration:g}"); + } + + return next(); + } +} +``` + +### Exception Handling in Post Workflow Middleware + +Post workflow middleware exception handling gets treated differently from pre workflow middleware. At the time that the workflow completes, your workflow has ran already so an uncaught exception would be difficult to act on. + +By default, if a workflow middleware throws an exception, it will be logged and the workflow will complete as normal. This behavior can be changed, however. + +To override the default post workflow error handling for all workflows in your app, just register a new `IWorkflowMiddlewareErrorHandler` in the dependency injection framework with your custom behavior as follows. + +```cs +// CustomMiddlewareErrorHandler.cs +public class CustomHandler : IWorkflowMiddlewareErrorHandler +{ + public Task HandleAsync(Exception ex) + { + // Handle your error asynchronously + } +} + +// Startup.cs +public void ConfigureServices(IServiceCollection services) +{ + // Other workflow configuration + services.AddWorkflow(); + + // Should go after .AddWorkflow() + services.AddTransient(); +} +``` + +## Registering Middleware + +In order for middleware to take effect, they must be registered with the built-in dependency injection framework using the convenience helpers. + +**Note:** Middleware will be run in the order that they are registered with middleware that are registered earlier running earlier in the chain and finishing later in the chain. For pre/post workflow middleware, all pre middleware will be run before a workflow starts and all post middleware will be run after a workflow completes. + +```cs +public class Startup +{ + public void ConfigureServices(IServiceCollection services) + { + ... + + // Add workflow middleware + services.AddWorkflowMiddleware(); + services.AddWorkflowMiddleware(); + + // Add step middleware + services.AddWorkflowStepMiddleware(); + services.AddWorkflowStepMiddleware(); + + ... + } +} +``` + +## More Information + +See the [Workflow Middleware](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample19) sample for full examples of workflow middleware in action. diff --git a/mkdocs.yml b/mkdocs.yml index fcbf9e4a8..57ed12c94 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,6 +9,7 @@ nav: - Saga transactions: sagas.md - JSON / YAML Definitions: json-yaml.md - Persistence: persistence.md + - Middleware: workflow-middleware.md - Multi-node clusters: multi-node-clusters.md - ASP.NET Core: using-with-aspnet-core.md - Elasticsearch plugin: elastic-search.md diff --git a/src/WorkflowCore.DSL/Models/v1/DefinitionSourceV1.cs b/src/WorkflowCore.DSL/Models/v1/DefinitionSourceV1.cs index 1de64c35e..46f0521ca 100644 --- a/src/WorkflowCore.DSL/Models/v1/DefinitionSourceV1.cs +++ b/src/WorkflowCore.DSL/Models/v1/DefinitionSourceV1.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.DefinitionStorage.v1 { diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index 810d6338b..0f5a89558 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -93,7 +93,7 @@ private WorkflowStepCollection ConvertSteps(ICollection source, Ty AttachInputs(nextStep, dataType, stepType, targetStep); AttachOutputs(nextStep, dataType, stepType, targetStep); - + if (nextStep.Do != null) { foreach (var branch in nextStep.Do) @@ -242,7 +242,7 @@ private void AttachOutcomes(StepSourceV1 source, Type dataType, WorkflowStep ste var outcomeParameter = Expression.Parameter(typeof(object), "outcome"); foreach (var nextStep in source.SelectNextStep) - { + { var sourceDelegate = DynamicExpressionParser.ParseLambda(new[] { dataParameter, outcomeParameter }, typeof(object), nextStep.Value).Compile(); Expression> sourceExpr = (data, outcome) => System.Convert.ToBoolean(sourceDelegate.DynamicInvoke(data, outcome)); step.Outcomes.Add(new ExpressionOutcome(sourceExpr) diff --git a/src/WorkflowCore/Interface/IStepExecutor.cs b/src/WorkflowCore/Interface/IStepExecutor.cs new file mode 100644 index 000000000..aed3d3026 --- /dev/null +++ b/src/WorkflowCore/Interface/IStepExecutor.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + /// + /// Executes a workflow step. + /// + public interface IStepExecutor + { + /// + /// Runs the passed in the given . + /// + /// The in which to execute the step. + /// The body. + /// A to wait for the result of running the step + Task ExecuteStep( + IStepExecutionContext context, + IStepBody body + ); + } +} diff --git a/src/WorkflowCore/Interface/IWorkflowBuilder.cs b/src/WorkflowCore/Interface/IWorkflowBuilder.cs index ec7b7bad2..b7b371882 100644 --- a/src/WorkflowCore/Interface/IWorkflowBuilder.cs +++ b/src/WorkflowCore/Interface/IWorkflowBuilder.cs @@ -9,7 +9,7 @@ public interface IWorkflowBuilder { List Steps { get; } - int LastStep { get; } + int LastStep { get; } IWorkflowBuilder UseData(); @@ -21,7 +21,7 @@ public interface IWorkflowBuilder } public interface IWorkflowBuilder : IWorkflowBuilder, IWorkflowModifier - { + { IStepBuilder StartWith(Action> stepSetup = null) where TStep : IStepBody; IStepBuilder StartWith(Func body); @@ -33,6 +33,5 @@ public interface IWorkflowBuilder : IWorkflowBuilder, IWorkflowModifier UseDefaultErrorBehavior(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null); IWorkflowBuilder CreateBranch(); - } -} \ No newline at end of file +} diff --git a/src/WorkflowCore/Interface/IWorkflowMiddleware.cs b/src/WorkflowCore/Interface/IWorkflowMiddleware.cs new file mode 100644 index 000000000..71781b30d --- /dev/null +++ b/src/WorkflowCore/Interface/IWorkflowMiddleware.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + /// + /// Determines at which point to run the middleware. + /// + public enum WorkflowMiddlewarePhase + { + /// + /// The middleware should run before a workflow starts. + /// + PreWorkflow, + + /// + /// The middleware should run after a workflow completes. + /// + PostWorkflow + } + + /// + /// Middleware that can run before a workflow starts or after a workflow completes. + /// + public interface IWorkflowMiddleware + { + /// + /// The phase in the workflow execution to run this middleware in + /// + WorkflowMiddlewarePhase Phase { get; } + + /// + /// Runs the middleware on the given . + /// + /// The . + /// The next middleware in the chain. + /// A that completes asynchronously once the + /// middleware chain finishes running. + Task HandleAsync(WorkflowInstance workflow, WorkflowDelegate next); + } +} diff --git a/src/WorkflowCore/Interface/IWorkflowMiddlewareErrorHandler.cs b/src/WorkflowCore/Interface/IWorkflowMiddlewareErrorHandler.cs new file mode 100644 index 000000000..5522bae39 --- /dev/null +++ b/src/WorkflowCore/Interface/IWorkflowMiddlewareErrorHandler.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading.Tasks; + +namespace WorkflowCore.Interface +{ + /// + /// Handles exceptions within workflow middleware. + /// + public interface IWorkflowMiddlewareErrorHandler + { + /// + /// Asynchronously handle the given exception. + /// + /// The exception to handle + /// A task that completes when handling is done. + Task HandleAsync(Exception ex); + } +} diff --git a/src/WorkflowCore/Interface/IWorkflowMiddlewareRunner.cs b/src/WorkflowCore/Interface/IWorkflowMiddlewareRunner.cs new file mode 100644 index 000000000..96aab8b74 --- /dev/null +++ b/src/WorkflowCore/Interface/IWorkflowMiddlewareRunner.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + /// + /// Runs workflow pre/post middleware. + /// + public interface IWorkflowMiddlewareRunner + { + /// + /// Runs workflow-level middleware that is set to run at the + /// phase. Middleware will be run in the + /// order in which they were registered with DI with middleware declared earlier starting earlier and + /// completing later. + /// + /// The to run for. + /// The definition. + /// A task that will complete when all middleware has run. + Task RunPreMiddleware(WorkflowInstance workflow, WorkflowDefinition def); + + /// + /// Runs workflow-level middleware that is set to run at the + /// phase. Middleware will be run in the + /// order in which they were registered with DI with middleware declared earlier starting earlier and + /// completing later. + /// + /// The to run for. + /// The definition. + /// A task that will complete when all middleware has run. + Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinition def); + } +} diff --git a/src/WorkflowCore/Interface/IWorkflowStepMiddleware.cs b/src/WorkflowCore/Interface/IWorkflowStepMiddleware.cs new file mode 100644 index 000000000..91f888589 --- /dev/null +++ b/src/WorkflowCore/Interface/IWorkflowStepMiddleware.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + /// + /// Middleware that runs around a workflow step and can enhance or alter + /// the steps behavior. + /// + public interface IWorkflowStepMiddleware + { + /// + /// Handle the workflow step and return an + /// asynchronously. It is important to invoke at some point + /// in the middleware. Not doing so will prevent the workflow step from ever + /// getting executed. + /// + /// The step's context. + /// An instance of the step body that is going to be run. + /// The next middleware in the chain. + /// A of the workflow result. + Task HandleAsync( + IStepExecutionContext context, + IStepBody body, + WorkflowStepDelegate next + ); + } +} diff --git a/src/WorkflowCore/Models/WorkflowDefinition.cs b/src/WorkflowCore/Models/WorkflowDefinition.cs index f39a563ed..207457e19 100644 --- a/src/WorkflowCore/Models/WorkflowDefinition.cs +++ b/src/WorkflowCore/Models/WorkflowDefinition.cs @@ -18,14 +18,16 @@ public class WorkflowDefinition public WorkflowErrorHandling DefaultErrorBehavior { get; set; } - public TimeSpan? DefaultErrorRetryInterval { get; set; } + public Type OnPostMiddlewareError { get; set; } + + public TimeSpan? DefaultErrorRetryInterval { get; set; } } - public enum WorkflowErrorHandling - { - Retry = 0, - Suspend = 1, + public enum WorkflowErrorHandling + { + Retry = 0, + Suspend = 1, Terminate = 2, Compensate = 3 } diff --git a/src/WorkflowCore/Models/WorkflowDelegate.cs b/src/WorkflowCore/Models/WorkflowDelegate.cs new file mode 100644 index 000000000..2d8876163 --- /dev/null +++ b/src/WorkflowCore/Models/WorkflowDelegate.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace WorkflowCore.Models +{ + /// + /// Represents a function that executes before or after a workflow starts or completes. + /// + public delegate Task WorkflowDelegate(); +} diff --git a/src/WorkflowCore/Models/WorkflowStepDelegate.cs b/src/WorkflowCore/Models/WorkflowStepDelegate.cs new file mode 100644 index 000000000..21befb575 --- /dev/null +++ b/src/WorkflowCore/Models/WorkflowStepDelegate.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace WorkflowCore.Models +{ + /// + /// Represents a function that executes a workflow step and returns a result. + /// + public delegate Task WorkflowStepDelegate(); +} diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 6fe511845..8598abe03 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -35,7 +35,7 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(); services.AddSingleton(options); - services.AddSingleton(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); @@ -51,6 +51,9 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddTransient(); services.AddTransient(); @@ -69,6 +72,40 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A return services; } + + /// + /// Adds a middleware that will run around the execution of a workflow step. + /// + /// The services collection. + /// Optionally configure using your own factory. + /// The type of middleware. + /// It must implement . + /// The services collection for chaining. + public static IServiceCollection AddWorkflowStepMiddleware( + this IServiceCollection services, + Func factory = null) + where TMiddleware : class, IWorkflowStepMiddleware => + factory == null + ? services.AddTransient() + : services.AddTransient(factory); + + /// + /// Adds a middleware that will run either before a workflow is kicked off or after + /// a workflow completes. Specify the phase of the workflow execution process that + /// you want to execute this middleware using . + /// + /// The services collection. + /// Optionally configure using your own factory. + /// The type of middleware. + /// It must implement . + /// The services collection for chaining. + public static IServiceCollection AddWorkflowMiddleware( + this IServiceCollection services, + Func factory = null) + where TMiddleware : class, IWorkflowMiddleware => + factory == null + ? services.AddTransient() + : services.AddTransient(factory); } } diff --git a/src/WorkflowCore/Services/DefaultWorkflowMiddlewareErrorHandler.cs b/src/WorkflowCore/Services/DefaultWorkflowMiddlewareErrorHandler.cs new file mode 100644 index 000000000..99c4652ff --- /dev/null +++ b/src/WorkflowCore/Services/DefaultWorkflowMiddlewareErrorHandler.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; + +namespace WorkflowCore.Services +{ + /// + /// Default implementation of . Just logs the + /// thrown exception and moves on. + /// + public class DefaultWorkflowMiddlewareErrorHandler : IWorkflowMiddlewareErrorHandler + { + private readonly ILogger _log; + + public DefaultWorkflowMiddlewareErrorHandler(ILogger log) + { + _log = log; + } + + /// + /// Asynchronously handle the given exception. + /// + /// The exception to handle + /// A task that completes when handling is done. + public Task HandleAsync(Exception ex) + { + _log.LogError(ex, "An error occurred running workflow middleware: {Message}", ex.Message); + return Task.CompletedTask; + } + } +} diff --git a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs index f5acd7694..98788fa0c 100644 --- a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs @@ -225,7 +225,7 @@ public IContainerStepBuilder ForEach(Expression ForEach(Expression> collection, Expression> runParallel) { return Start().ForEach(collection, runParallel); diff --git a/src/WorkflowCore/Services/StepExecutor.cs b/src/WorkflowCore/Services/StepExecutor.cs new file mode 100644 index 000000000..4e45574e5 --- /dev/null +++ b/src/WorkflowCore/Services/StepExecutor.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Services +{ + /// + /// Executes the workflow step and applies any to the step. + /// + public class StepExecutor : IStepExecutor + { + private readonly IEnumerable _stepMiddleware; + + public StepExecutor( + IEnumerable stepMiddleware + ) + { + _stepMiddleware = stepMiddleware; + } + + /// + /// Runs the passed in the given while applying + /// any registered in the system. Middleware will be run in the + /// order in which they were registered with DI with middleware declared earlier starting earlier and + /// completing later. + /// + /// The in which to execute the step. + /// The body. + /// A to wait for the result of running the step + public async Task ExecuteStep( + IStepExecutionContext context, + IStepBody body + ) + { + // Build the middleware chain by reducing over all the middleware in reverse starting with step body + // and building step delegates that call out to the next delegate in the chain + Task Step() => body.RunAsync(context); + var middlewareChain = _stepMiddleware + .Reverse() + .Aggregate( + (WorkflowStepDelegate) Step, + (previous, middleware) => () => middleware.HandleAsync(context, body, previous) + ); + + // Run the middleware chain + return await middlewareChain(); + } + } +} diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 203da3139..20b7621d9 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -21,9 +21,10 @@ public class WorkflowController : IWorkflowController private readonly IExecutionPointerFactory _pointerFactory; private readonly ILifeCycleEventHub _eventHub; private readonly IServiceProvider _serviceProvider; + private readonly IWorkflowMiddlewareRunner _middlewareRunner; private readonly ILogger _logger; - public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory, IServiceProvider serviceProvider) + public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowMiddlewareRunner middlewareRunner) { _persistenceStore = persistenceStore; _lockProvider = lockProvider; @@ -32,6 +33,7 @@ public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLoc _pointerFactory = pointerFactory; _eventHub = eventHub; _serviceProvider = serviceProvider; + _middlewareRunner = middlewareRunner; _logger = loggerFactory.CreateLogger(); } @@ -45,7 +47,7 @@ public Task StartWorkflow(string workflowId, int? version, object data = return StartWorkflow(workflowId, version, data, reference); } - public Task StartWorkflow(string workflowId, TData data = null, string reference=null) + public Task StartWorkflow(string workflowId, TData data = null, string reference=null) where TData : class, new() { return StartWorkflow(workflowId, null, data, reference); @@ -83,6 +85,8 @@ public async Task StartWorkflow(string workflowId, int? version, wf.ExecutionPointers.Add(_pointerFactory.BuildGenesisPointer(def)); + await _middlewareRunner.RunPreMiddleware(wf, def); + string id = await _persistenceStore.CreateNewWorkflow(wf); await _queueProvider.QueueWork(id, QueueType.Workflow); await _queueProvider.QueueWork(id, QueueType.Index); @@ -230,4 +234,4 @@ public void RegisterWorkflow() _registry.RegisterWorkflow(wf); } } -} \ No newline at end of file +} diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index e557a2bb2..a70de87a2 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -22,10 +22,12 @@ public class WorkflowExecutor : IWorkflowExecutor private readonly ICancellationProcessor _cancellationProcessor; private readonly ILifeCycleEventPublisher _publisher; private readonly WorkflowOptions _options; + private readonly IStepExecutor _stepExecutor; + private readonly IWorkflowMiddlewareRunner _middlewareRunner; private IWorkflowHost Host => _serviceProvider.GetService(); - public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IScopeProvider scopeProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, ICancellationProcessor cancellationProcessor, WorkflowOptions options, ILoggerFactory loggerFactory) + public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IScopeProvider scopeProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, ICancellationProcessor cancellationProcessor, WorkflowOptions options, IWorkflowMiddlewareRunner middlewareRunner, IStepExecutor stepExecutor, ILoggerFactory loggerFactory) { _serviceProvider = serviceProvider; _scopeProvider = scopeProvider; @@ -36,6 +38,8 @@ public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProv _options = options; _logger = loggerFactory.CreateLogger(); _executionResultProcessor = executionResultProcessor; + _middlewareRunner = middlewareRunner; + _stepExecutor = stepExecutor; } public async Task Execute(WorkflowInstance workflow, CancellationToken cancellationToken = default) @@ -49,7 +53,7 @@ public async Task Execute(WorkflowInstance workflow, Can _logger.LogError("Workflow {0} version {1} is not registered", workflow.WorkflowDefinitionId, workflow.Version); return wfResult; } - + _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); foreach (var pointer in exePointers) @@ -71,10 +75,10 @@ public async Task Execute(WorkflowInstance workflow, Can }); continue; } - + try { - if (!InitializeStep(workflow, step, wfResult, def, pointer)) + if (!InitializeStep(workflow, step, wfResult, def, pointer)) continue; await ExecuteStep(workflow, step, pointer, wfResult, def, cancellationToken); @@ -89,14 +93,14 @@ public async Task Execute(WorkflowInstance workflow, Can ErrorTime = _datetimeProvider.UtcNow, Message = ex.Message }); - + _executionResultProcessor.HandleStepException(workflow, def, pointer, step, ex); Host.ReportStepError(workflow, step, ex); } _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); } ProcessAfterExecutionIteration(workflow, def, wfResult); - DetermineNextExecutionTime(workflow); + await DetermineNextExecutionTime(workflow, def); return wfResult; } @@ -147,7 +151,7 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe Item = pointer.ContextItem, CancellationToken = cancellationToken }; - + using (var scope = _scopeProvider.CreateScope(context)) { _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); @@ -181,7 +185,7 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe return; } - var result = await body.RunAsync(context); + var result = await _stepExecutor.ExecuteStep(context, body); if (result.Proceed) { @@ -205,7 +209,7 @@ private void ProcessAfterExecutionIteration(WorkflowInstance workflow, WorkflowD } } - private void DetermineNextExecutionTime(WorkflowInstance workflow) + private async Task DetermineNextExecutionTime(WorkflowInstance workflow, WorkflowDefinition def) { //TODO: move to own class workflow.NextExecution = null; @@ -229,9 +233,9 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) { foreach (var pointer in workflow.ExecutionPointers.Where(x => x.Active && (x.Children ?? new List()).Count > 0)) { - if (!workflow.ExecutionPointers.FindByScope(pointer.Id).All(x => x.EndTime.HasValue)) + if (!workflow.ExecutionPointers.FindByScope(pointer.Id).All(x => x.EndTime.HasValue)) continue; - + if (!pointer.SleepUntil.HasValue) { workflow.NextExecution = 0; @@ -243,11 +247,14 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) } } - if ((workflow.NextExecution != null) || (workflow.ExecutionPointers.Any(x => x.EndTime == null))) + if ((workflow.NextExecution != null) || (workflow.ExecutionPointers.Any(x => x.EndTime == null))) return; - + workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.UtcNow; + + await _middlewareRunner.RunPostMiddleware(workflow, def); + _publisher.PublishNotification(new WorkflowCompleted() { EventTimeUtc = _datetimeProvider.UtcNow, @@ -257,6 +264,5 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow) Version = workflow.Version }); } - } } diff --git a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs new file mode 100644 index 000000000..6eb6968b8 --- /dev/null +++ b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Services +{ + /// + public class WorkflowMiddlewareRunner : IWorkflowMiddlewareRunner + { + private static readonly WorkflowDelegate NoopWorkflowDelegate = () => Task.CompletedTask; + private readonly IEnumerable _middleware; + private readonly IServiceProvider _serviceProvider; + + public WorkflowMiddlewareRunner( + IEnumerable middleware, + IServiceProvider serviceProvider + ) + { + _middleware = middleware; + _serviceProvider = serviceProvider; + } + + + /// + /// Runs workflow-level middleware that is set to run at the + /// phase. Middleware will be run in the + /// order in which they were registered with DI with middleware declared earlier starting earlier and + /// completing later. + /// + /// The to run for. + /// The definition. + /// A task that will complete when all middleware has run. + public async Task RunPreMiddleware(WorkflowInstance workflow, WorkflowDefinition def) + { + var preMiddleware = _middleware + .Where(m => m.Phase == WorkflowMiddlewarePhase.PreWorkflow) + .ToArray(); + + await RunWorkflowMiddleware(workflow, preMiddleware); + } + + /// + /// Runs workflow-level middleware that is set to run at the + /// phase. Middleware will be run in the + /// order in which they were registered with DI with middleware declared earlier starting earlier and + /// completing later. + /// + /// The to run for. + /// The definition. + /// A task that will complete when all middleware has run. + public async Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinition def) + { + var postMiddleware = _middleware + .Where(m => m.Phase == WorkflowMiddlewarePhase.PostWorkflow) + .ToArray(); + + try + { + await RunWorkflowMiddleware(workflow, postMiddleware); + } + catch (Exception exception) + { + // On error, determine which error handler to run and then run it + var errorHandlerType = def.OnPostMiddlewareError ?? typeof(IWorkflowMiddlewareErrorHandler); + using (var scope = _serviceProvider.CreateScope()) + { + var typeInstance = scope.ServiceProvider.GetService(errorHandlerType); + if (typeInstance != null && typeInstance is IWorkflowMiddlewareErrorHandler handler) + { + await handler.HandleAsync(exception); + } + } + } + } + + private static async Task RunWorkflowMiddleware( + WorkflowInstance workflow, + IEnumerable middlewareCollection + ) + { + // Build the middleware chain + var middlewareChain = middlewareCollection + .Reverse() + .Aggregate( + NoopWorkflowDelegate, + (previous, middleware) => () => middleware.HandleAsync(workflow, previous) + ); + + await middlewareChain(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample19/FlakyConnectionParams.cs b/src/samples/WorkflowCore.Sample19/FlakyConnectionParams.cs new file mode 100644 index 000000000..0fda1a91f --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/FlakyConnectionParams.cs @@ -0,0 +1,9 @@ +using System; + +namespace WorkflowCore.Sample19 +{ + public class FlakyConnectionParams : IDescriptiveWorkflowParams + { + public string Description { get; set; } + } +} diff --git a/src/samples/WorkflowCore.Sample19/FlakyConnectionWorkflow.cs b/src/samples/WorkflowCore.Sample19/FlakyConnectionWorkflow.cs new file mode 100644 index 000000000..a388930d2 --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/FlakyConnectionWorkflow.cs @@ -0,0 +1,25 @@ +using WorkflowCore.Interface; +using WorkflowCore.Sample19.Steps; + +namespace WorkflowCore.Sample19 +{ + public class FlakyConnectionWorkflow : IWorkflow + { + public string Id => "flaky-sample"; + + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Input(x => x.Message, _ => "Starting workflow") + + .Then() + .Input(x => x.SucceedAfterAttempts, _ => 3) + + .Then() + .Input(x => x.Message, _ => "Finishing workflow"); + } + } +} diff --git a/src/samples/WorkflowCore.Sample19/IDescriptiveWorkflowParams.cs b/src/samples/WorkflowCore.Sample19/IDescriptiveWorkflowParams.cs new file mode 100644 index 000000000..73bd26892 --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/IDescriptiveWorkflowParams.cs @@ -0,0 +1,7 @@ +namespace WorkflowCore.Sample19 +{ + public interface IDescriptiveWorkflowParams + { + string Description { get; } + } +} diff --git a/src/samples/WorkflowCore.Sample19/Middleware/AddDescriptionWorkflowMiddleware.cs b/src/samples/WorkflowCore.Sample19/Middleware/AddDescriptionWorkflowMiddleware.cs new file mode 100644 index 000000000..8c6080c12 --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/Middleware/AddDescriptionWorkflowMiddleware.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample19.Middleware +{ + public class AddDescriptionWorkflowMiddleware : IWorkflowMiddleware + { + public WorkflowMiddlewarePhase Phase => WorkflowMiddlewarePhase.PreWorkflow; + public Task HandleAsync(WorkflowInstance workflow, WorkflowDelegate next) + { + if (workflow.Data is IDescriptiveWorkflowParams descriptiveParams) + { + workflow.Description = descriptiveParams.Description; + } + + return next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample19/Middleware/LogCorrelationStepMiddleware.cs b/src/samples/WorkflowCore.Sample19/Middleware/LogCorrelationStepMiddleware.cs new file mode 100644 index 000000000..38fefc362 --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/Middleware/LogCorrelationStepMiddleware.cs @@ -0,0 +1,38 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample19.Middleware +{ + /// + /// Loosely based off this article: + /// https://www.frakkingsweet.com/net-core-log-correlation-easy-access-to-headers/ + /// + public class AddMetadataToLogsMiddleware: IWorkflowStepMiddleware + { + private readonly ILogger _log; + + public AddMetadataToLogsMiddleware(ILogger log) + { + _log = log; + } + + public async Task HandleAsync( + IStepExecutionContext context, + IStepBody body, + WorkflowStepDelegate next) + { + var workflowId = context.Workflow.Id; + var stepId = context.Step.Id; + + using (_log.BeginScope("WorkflowId => {@WorkflowId}", workflowId)) + using (_log.BeginScope("StepId => {@StepId}", stepId)) + { + return await next(); + } + } + } +} diff --git a/src/samples/WorkflowCore.Sample19/Middleware/PollyRetryMiddleware.cs b/src/samples/WorkflowCore.Sample19/Middleware/PollyRetryMiddleware.cs new file mode 100644 index 000000000..24e7621e7 --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/Middleware/PollyRetryMiddleware.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Polly; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample19.Middleware +{ + public class PollyRetryMiddleware : IWorkflowStepMiddleware + { + private const string StepContextKey = "WorkflowStepContext"; + private const int MaxRetries = 3; + private readonly ILogger _log; + + public PollyRetryMiddleware(ILogger log) + { + _log = log; + } + + public IAsyncPolicy GetRetryPolicy() => + Policy + .Handle() + .RetryAsync( + MaxRetries, + (result, retryCount, context) => + UpdateRetryCount(result.Exception, retryCount, context[StepContextKey] as IStepExecutionContext) + ); + + public async Task HandleAsync( + IStepExecutionContext context, + IStepBody body, + WorkflowStepDelegate next + ) + { + return await GetRetryPolicy().ExecuteAsync(ctx => next(), new Dictionary + { + { StepContextKey, context } + }); + } + + private Task UpdateRetryCount( + Exception exception, + int retryCount, + IStepExecutionContext stepContext) + { + var stepInstance = stepContext.ExecutionPointer; + stepInstance.RetryCount = retryCount; + + _log.LogWarning( + exception, + "Exception occurred in step {StepId}. Retrying [{RetryCount}/{MaxCount}]", + stepInstance.Id, + retryCount, + MaxRetries + ); + + // TODO: Come up with way to persist workflow + return Task.CompletedTask; + } + } +} diff --git a/src/samples/WorkflowCore.Sample19/Middleware/PrintWorkflowSummaryMiddleware.cs b/src/samples/WorkflowCore.Sample19/Middleware/PrintWorkflowSummaryMiddleware.cs new file mode 100644 index 000000000..2cdba963e --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/Middleware/PrintWorkflowSummaryMiddleware.cs @@ -0,0 +1,41 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample19.Middleware +{ + public class PrintWorkflowSummaryMiddleware : IWorkflowMiddleware + { + private readonly ILogger _log; + + public PrintWorkflowSummaryMiddleware(ILogger log) + { + _log = log; + } + + public WorkflowMiddlewarePhase Phase => WorkflowMiddlewarePhase.PostWorkflow; + + public Task HandleAsync(WorkflowInstance workflow, WorkflowDelegate next) + { + if (!workflow.CompleteTime.HasValue) + { + return next(); + } + + var duration = workflow.CompleteTime.Value - workflow.CreateTime; + _log.LogInformation($@"Workflow {workflow.Description} completed in {duration:g}"); + + foreach (var step in workflow.ExecutionPointers) + { + var stepName = step.StepName; + var stepDuration = (step.EndTime - step.StartTime) ?? TimeSpan.Zero; + _log.LogInformation($" - Step {stepName} completed in {stepDuration:g}"); + } + + return next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample19/Program.cs b/src/samples/WorkflowCore.Sample19/Program.cs new file mode 100644 index 000000000..1d6e38a48 --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/Program.cs @@ -0,0 +1,66 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using Microsoft.Extensions.Logging.Console; +using WorkflowCore.Interface; +using WorkflowCore.Sample19.Middleware; +using WorkflowCore.Sample19.Steps; + +namespace WorkflowCore.Sample19 +{ + class Program + { + static void Main(string[] args) + { + var serviceProvider = ConfigureServices(); + + // Start the workflow host + var host = serviceProvider.GetService(); + host.RegisterWorkflow(); + host.Start(); + + var workflowParams = new FlakyConnectionParams + { + Description = "Flaky connection workflow" + }; + var workflowId = host.StartWorkflow("flaky-sample", workflowParams).Result; + Console.WriteLine($"Kicked off workflow {workflowId}"); + + Console.ReadLine(); + host.Stop(); + } + + private static IServiceProvider ConfigureServices() + { + // Setup dependency injection + IServiceCollection services = new ServiceCollection(); + services.AddWorkflow(); + + // Add step middleware + // Note that middleware will get executed in the order in which they were registered + services.AddWorkflowStepMiddleware(); + services.AddWorkflowStepMiddleware(); + + // Add some pre workflow middleware + // This middleware will run before the workflow starts + services.AddWorkflowMiddleware(); + + // Add some post workflow middleware + // This middleware will run after the workflow completes + services.AddWorkflowMiddleware(); + + // Add workflow steps + services.AddTransient(); + services.AddTransient(); + + services.AddLogging(cfg => + { + cfg.AddConsole(x => x.IncludeScopes = true); + cfg.AddDebug(); + }); + + var serviceProvider = services.BuildServiceProvider(); + return serviceProvider; + } + } +} diff --git a/src/samples/WorkflowCore.Sample19/Steps/FlakyConnection.cs b/src/samples/WorkflowCore.Sample19/Steps/FlakyConnection.cs new file mode 100644 index 000000000..f04f33f11 --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/Steps/FlakyConnection.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample19.Steps +{ + public class FlakyConnection : StepBodyAsync + { + private static readonly TimeSpan Delay = TimeSpan.FromSeconds(1); + private int _currentCallCount = 0; + + public int? SucceedAfterAttempts { get; set; } = 3; + + public override async Task RunAsync(IStepExecutionContext context) + { + if (SucceedAfterAttempts.HasValue && _currentCallCount >= SucceedAfterAttempts.Value) + { + return ExecutionResult.Next(); + } + + _currentCallCount++; + await Task.Delay(Delay); + throw new TimeoutException("A call has timed out"); + } + } +} diff --git a/src/samples/WorkflowCore.Sample19/Steps/LogMessage.cs b/src/samples/WorkflowCore.Sample19/Steps/LogMessage.cs new file mode 100644 index 000000000..ac8f4b678 --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/Steps/LogMessage.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample19.Steps +{ + public class LogMessage : StepBodyAsync + { + private readonly ILogger _log; + + public LogMessage(ILogger log) + { + _log = log; + } + + public string Message { get; set; } + + public override Task RunAsync(IStepExecutionContext context) + { + if (Message != null) + { + _log.LogInformation(Message); + } + + return Task.FromResult(ExecutionResult.Next()); + } + } +} diff --git a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj new file mode 100644 index 000000000..b2a7796c3 --- /dev/null +++ b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj @@ -0,0 +1,20 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + + + + + + + + diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/MiddlewareScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/MiddlewareScenario.cs new file mode 100644 index 000000000..1c6929209 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/MiddlewareScenario.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Testing; +using Xunit; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class MiddlewareScenario : WorkflowTest + { + private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(60); + private static readonly TimeSpan Delay = TimeSpan.FromMilliseconds(5); + private readonly List _workflowMiddleware = new List(); + private readonly List _stepMiddleware = new List(); + private readonly TestStep _step = new TestStep(); + + public MiddlewareScenario() + { + Setup(); + } + + public TestWorkflowMiddleware[] PreMiddleware => _workflowMiddleware + .Where(x => x.Phase == WorkflowMiddlewarePhase.PreWorkflow) + .ToArray(); + + public TestWorkflowMiddleware[] PostMiddleware => _workflowMiddleware + .Where(x => x.Phase == WorkflowMiddlewarePhase.PostWorkflow) + .ToArray(); + + public class MyWorkflow: IWorkflow + { + public string Id => nameof(MyWorkflow); + + public int Version => 1; + + public void Build(IWorkflowBuilder builder) => + builder.StartWith(); + } + + public class TestStep : StepBodyAsync + { + + public DateTime? StartTime { get; private set; } + public DateTime? EndTime { get; private set; } + public bool HasCompleted => StartTime.HasValue && EndTime.HasValue; + + public override async Task RunAsync(IStepExecutionContext context) + { + StartTime = DateTime.UtcNow; + await Task.Delay(Delay); + EndTime = DateTime.UtcNow; + return ExecutionResult.Next(); + } + } + + public class TestWorkflowMiddleware : IWorkflowMiddleware + { + public TestWorkflowMiddleware(WorkflowMiddlewarePhase phase) + { + Phase = phase; + } + + public WorkflowMiddlewarePhase Phase { get; } + + public DateTime? StartTime { get; private set; } + public DateTime? EndTime { get; private set; } + public bool HasCompleted => StartTime.HasValue && EndTime.HasValue; + + public async Task HandleAsync(WorkflowInstance workflow, WorkflowDelegate next) + { + StartTime = DateTime.UtcNow; + await Task.Delay(Delay); + await next(); + await Task.Delay(Delay); + EndTime = DateTime.UtcNow; + } + } + + public class TestStepMiddleware : IWorkflowStepMiddleware + { + public DateTime? StartTime { get; private set; } + public DateTime? EndTime { get; private set; } + + public bool HasCompleted => StartTime.HasValue && EndTime.HasValue; + + public async Task HandleAsync(IStepExecutionContext context, IStepBody body, WorkflowStepDelegate next) + { + StartTime = DateTime.UtcNow; + await Task.Delay(Delay); + var result = await next(); + await Task.Delay(Delay); + EndTime = DateTime.UtcNow; + return result; + } + } + + protected override void ConfigureServices(IServiceCollection services) + { + base.ConfigureServices(services); + + services.AddTransient(_ => _step); + + // Configure 3 middleware of each type + const int middlewareCount = 3; + foreach (var _ in Enumerable.Range(0, middlewareCount)) + { + var preMiddleware = new TestWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow); + var postMiddleware = new TestWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow); + _workflowMiddleware.Add(preMiddleware); + _workflowMiddleware.Add(postMiddleware); + services.AddWorkflowMiddleware(p => preMiddleware); + services.AddWorkflowMiddleware(p => postMiddleware); + } + + // Configure 3 step middleware + foreach (var _ in Enumerable.Range(0, middlewareCount)) + { + var middleware = new TestStepMiddleware(); + services.AddWorkflowStepMiddleware(p => middleware); + _stepMiddleware.Add(middleware); + } + + } + + [Fact(DisplayName = "Should run all workflow and step middleware")] + public async Task Should_run_all_workflow_and_step_middleware() + { + var workflowId = await StartWorkflowAsync(new object()); + var status = await WaitForWorkflowToCompleteAsync(workflowId, Timeout); + + // Workflow should complete without errors + status.Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + + // Each middleware should have run + _workflowMiddleware.Should() + .HaveCount(6).And + .OnlyContain(x => x.HasCompleted); + _stepMiddleware.Should() + .HaveCount(3) + .And + .OnlyContain(x => x.HasCompleted); + + // Step middleware should have been run in order + _stepMiddleware.Should().BeInAscendingOrder(x => x.StartTime); + _stepMiddleware.Should().BeInDescendingOrder(x => x.EndTime); + + // Step should have been called after all step middleware + _step.HasCompleted.Should().BeTrue(); + _step.StartTime.Should().BeAfter(_stepMiddleware.Last().StartTime.Value); + _step.EndTime.Should().BeBefore(_stepMiddleware.Last().EndTime.Value); + + // Pre workflow middleware should have been run in order + PreMiddleware.Should().BeInAscendingOrder(x => x.StartTime); + PreMiddleware.Should().BeInDescendingOrder(x => x.EndTime); + + // Post workflow middleware should have been run in order + PostMiddleware.Should().BeInAscendingOrder(x => x.StartTime); + PostMiddleware.Should().BeInDescendingOrder(x => x.EndTime); + } + } +} diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs index fb7d2eed1..7d8a0117a 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs @@ -5,13 +5,14 @@ using WorkflowCore.Models; using Xunit; using FluentAssertions; +using WorkflowCore.Services.DefinitionStorage; using WorkflowCore.Testing; using WorkflowCore.TestAssets.DataTypes; namespace WorkflowCore.IntegrationTests.Scenarios { public class StoredJsonScenario : JsonWorkflowTest - { + { public StoredJsonScenario() { Setup(); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs index 820f86bd8..3b37d2eb7 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs @@ -5,13 +5,14 @@ using WorkflowCore.Models; using Xunit; using FluentAssertions; +using WorkflowCore.Services.DefinitionStorage; using WorkflowCore.Testing; using WorkflowCore.TestAssets.DataTypes; namespace WorkflowCore.IntegrationTests.Scenarios { public class StoredYamlScenario : YamlWorkflowTest - { + { public StoredYamlScenario() { Setup(); diff --git a/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs b/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs index 7f1eebf1d..677fb511c 100644 --- a/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs +++ b/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs @@ -2,12 +2,13 @@ using System.Collections.Generic; using System.Text; using System.Threading; +using System.Threading.Tasks; using WorkflowCore.Interface; using FluentAssertions; using NUnit.Framework; namespace WorkflowCore.TestAssets.LockProvider -{ +{ public abstract class DistributedLockProviderTests { protected IDistributedLockProvider Subject; @@ -19,10 +20,10 @@ public void Setup() Subject.Start(); } - protected abstract IDistributedLockProvider CreateProvider(); + protected abstract IDistributedLockProvider CreateProvider(); [Test] - public async void AcquiresLock() + public async Task AcquiresLock() { const string lock1 = "lock1"; const string lock2 = "lock2"; @@ -34,7 +35,7 @@ public async void AcquiresLock() } [Test] - public async void DoesNotAcquireWhenLocked() + public async Task DoesNotAcquireWhenLocked() { const string lock1 = "lock1"; await Subject.AcquireLock(lock1, new CancellationToken()); @@ -45,7 +46,7 @@ public async void DoesNotAcquireWhenLocked() } [Test] - public async void ReleasesLock() + public async Task ReleasesLock() { const string lock1 = "lock1"; await Subject.AcquireLock(lock1, new CancellationToken()); diff --git a/test/WorkflowCore.TestAssets/stored-definition.json b/test/WorkflowCore.TestAssets/stored-definition.json index 56f36ac5e..1db3a9174 100644 --- a/test/WorkflowCore.TestAssets/stored-definition.json +++ b/test/WorkflowCore.TestAssets/stored-definition.json @@ -90,4 +90,4 @@ } ] -} \ No newline at end of file +} diff --git a/test/WorkflowCore.Testing/JsonWorkflowTest.cs b/test/WorkflowCore.Testing/JsonWorkflowTest.cs index 16fd7c232..760966a52 100644 --- a/test/WorkflowCore.Testing/JsonWorkflowTest.cs +++ b/test/WorkflowCore.Testing/JsonWorkflowTest.cs @@ -17,6 +17,7 @@ public abstract class JsonWorkflowTest : IDisposable protected IWorkflowHost Host; protected IPersistenceProvider PersistenceProvider; protected IDefinitionLoader DefinitionLoader; + protected IWorkflowRegistry Registry; protected List UnhandledStepErrors = new List(); protected virtual void Setup() @@ -34,6 +35,7 @@ protected virtual void Setup() PersistenceProvider = serviceProvider.GetService(); DefinitionLoader = serviceProvider.GetService(); + Registry = serviceProvider.GetService(); Host = serviceProvider.GetService(); Host.OnStepError += Host_OnStepError; Host.Start(); @@ -106,5 +108,5 @@ public void Dispose() Host.Stop(); } } - + } diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/test/WorkflowCore.Testing/WorkflowTest.cs index 1a0dc8d81..f61d6be0a 100644 --- a/test/WorkflowCore.Testing/WorkflowTest.cs +++ b/test/WorkflowCore.Testing/WorkflowTest.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using WorkflowCore.Interface; @@ -18,7 +19,7 @@ public abstract class WorkflowTest : IDisposable protected IWorkflowHost Host; protected IPersistenceProvider PersistenceProvider; protected List UnhandledStepErrors = new List(); - + protected virtual void Setup() { //setup dependency injection @@ -61,6 +62,13 @@ public string StartWorkflow(TData data) return workflowId; } + public async Task StartWorkflowAsync(TData data) + { + var def = new TWorkflow(); + var workflowId = await Host.StartWorkflow(def.Id, data); + return workflowId; + } + protected void WaitForWorkflowToComplete(string workflowId, TimeSpan timeOut) { var status = GetStatus(workflowId); @@ -73,6 +81,20 @@ protected void WaitForWorkflowToComplete(string workflowId, TimeSpan timeOut) } } + protected async Task WaitForWorkflowToCompleteAsync(string workflowId, TimeSpan timeOut) + { + var status = GetStatus(workflowId); + var counter = 0; + while ((status == WorkflowStatus.Runnable) && (counter < (timeOut.TotalMilliseconds / 100))) + { + await Task.Delay(100); + counter++; + status = GetStatus(workflowId); + } + + return status; + } + protected IEnumerable GetActiveSubscriptons(string eventName, string eventKey) { return PersistenceProvider.GetSubscriptions(eventName, eventKey, DateTime.MaxValue).Result; diff --git a/test/WorkflowCore.Testing/YamlWorkflowTest.cs b/test/WorkflowCore.Testing/YamlWorkflowTest.cs index 58cbb3cad..b16d07a6d 100644 --- a/test/WorkflowCore.Testing/YamlWorkflowTest.cs +++ b/test/WorkflowCore.Testing/YamlWorkflowTest.cs @@ -17,6 +17,7 @@ public abstract class YamlWorkflowTest : IDisposable protected IWorkflowHost Host; protected IPersistenceProvider PersistenceProvider; protected IDefinitionLoader DefinitionLoader; + protected IWorkflowRegistry Registry; protected List UnhandledStepErrors = new List(); protected virtual void Setup() @@ -34,6 +35,7 @@ protected virtual void Setup() PersistenceProvider = serviceProvider.GetService(); DefinitionLoader = serviceProvider.GetService(); + Registry = serviceProvider.GetService(); Host = serviceProvider.GetService(); Host.OnStepError += Host_OnStepError; Host.Start(); @@ -106,5 +108,4 @@ public void Dispose() Host.Stop(); } } - } diff --git a/test/WorkflowCore.UnitTests/Services/StepExecutorTests.cs b/test/WorkflowCore.UnitTests/Services/StepExecutorTests.cs new file mode 100644 index 000000000..86d2bfcee --- /dev/null +++ b/test/WorkflowCore.UnitTests/Services/StepExecutorTests.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; +using FakeItEasy; +using FluentAssertions; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Services; +using Xunit; +using Xunit.Abstractions; + +namespace WorkflowCore.UnitTests.Services +{ + public class StepExecutorTests + { + protected List Middleware { get; } + protected IStepBody Body { get; } + protected IStepExecutionContext Context { get; } + protected IStepExecutor Runner { get; } + protected ExecutionResult DummyResult { get; } = ExecutionResult.Persist(null); + protected ITestOutputHelper Out { get; } + + public StepExecutorTests(ITestOutputHelper output) + { + Middleware = new List(); + Body = A.Fake(); + Context = A.Fake(); + Out = output; + Runner = new StepExecutor(Middleware); + + A + .CallTo(() => Body.RunAsync(A._)) + .Invokes(() => Out.WriteLine("Called step body")) + .Returns(DummyResult); + } + + [Fact(DisplayName = "ExecuteStep should run step when no middleware")] + public async Task ExecuteStep_should_run_step_when_no_middleware() + { + // Act + var result = await Runner.ExecuteStep(Context, Body); + + // Assert + result.Should().Be(DummyResult); + } + + [Fact(DisplayName = "ExecuteStep should run middleware and step when one middleware")] + public async Task ExecuteStep_should_run_middleware_and_step_when_one_middleware() + { + // Arrange + var middleware = BuildStepMiddleware(); + Middleware.Add(middleware); + + // Act + var result = await Runner.ExecuteStep(Context, Body); + + // Assert + result.Should().Be(DummyResult); + A + .CallTo(RunMethodFor(Body)) + .MustHaveHappenedOnceExactly() + .Then( + A.CallTo(HandleMethodFor(middleware)) + .MustHaveHappenedOnceExactly()); + } + + [Fact(DisplayName = + "ExecuteStep should run middleware chain completing in reverse order and step when multiple middleware")] + public async Task + ExecuteStep_should_run_middleware_chain_completing_in_reverse_order_and_step_when_multiple_middleware() + { + // Arrange + var middleware1 = BuildStepMiddleware(1); + var middleware2 = BuildStepMiddleware(2); + var middleware3 = BuildStepMiddleware(3); + Middleware.AddRange(new[] { middleware1, middleware2, middleware3 }); + + // Act + var result = await Runner.ExecuteStep(Context, Body); + + // Assert + result.Should().Be(DummyResult); + A + .CallTo(RunMethodFor(Body)) + .MustHaveHappenedOnceExactly() + .Then(A + .CallTo(HandleMethodFor(middleware3)) + .MustHaveHappenedOnceExactly()) + .Then(A + .CallTo(HandleMethodFor(middleware2)) + .MustHaveHappenedOnceExactly()) + .Then(A + .CallTo(HandleMethodFor(middleware1)) + .MustHaveHappenedOnceExactly()); + } + + [Fact(DisplayName = "ExecuteStep should bubble up exceptions in middleware")] + public void ExecuteStep_should_bubble_up_exceptions_in_middleware() + { + // Arrange + var middleware1 = BuildStepMiddleware(1); + var middleware2 = BuildStepMiddleware(2); + var middleware3 = BuildStepMiddleware(3); + Middleware.AddRange(new[] { middleware1, middleware2, middleware3 }); + A + .CallTo(HandleMethodFor(middleware2)) + .Throws(new ApplicationException("Failed")); + + // Act + Func> action = async () => await Runner.ExecuteStep(Context, Body); + + // Assert + action + .ShouldThrow() + .WithMessage("Failed"); + } + + #region Helpers + + private IWorkflowStepMiddleware BuildStepMiddleware(int id = 0) + { + var middleware = A.Fake(); + A + .CallTo(HandleMethodFor(middleware)) + .ReturnsLazily(async call => + { + Out.WriteLine($@"Before step middleware {id}"); + var result = await call.Arguments[2].As().Invoke(); + Out.WriteLine($@"After step middleware {id}"); + return result; + }); + + return middleware; + } + + private static Expression>> HandleMethodFor(IWorkflowStepMiddleware middleware) => + () => middleware.HandleAsync( + A._, + A._, + A._); + + private static Expression>> RunMethodFor(IStepBody body) => + () => body.RunAsync(A._); + + #endregion + } +} diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 622af03ff..8b6f22e22 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -3,6 +3,8 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Threading.Tasks; +using FluentAssertions; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Services; @@ -22,6 +24,8 @@ public class WorkflowExecutorFixture protected IServiceProvider ServiceProvider; protected IScopeProvider ScopeProvider; protected IDateTimeProvider DateTimeProvider; + protected IStepExecutor StepExecutor; + protected IWorkflowMiddlewareRunner MiddlewareRunner; protected WorkflowOptions Options; public WorkflowExecutorFixture() @@ -35,6 +39,8 @@ public WorkflowExecutorFixture() EventHub = A.Fake(); CancellationProcessor = A.Fake(); DateTimeProvider = A.Fake(); + MiddlewareRunner = A.Fake(); + StepExecutor = A.Fake(); Options = new WorkflowOptions(A.Fake()); @@ -45,17 +51,26 @@ public WorkflowExecutorFixture() A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); A.CallTo(() => DateTimeProvider.UtcNow).Returns(DateTime.UtcNow); + A.CallTo(() => MiddlewareRunner + .RunPostMiddleware(A._, A._)) + .Returns(Task.CompletedTask); + + A.CallTo(() => StepExecutor.ExecuteStep(A._, A._)) + .ReturnsLazily(call => + call.Arguments[1].As().RunAsync( + call.Arguments[0].As())); + //config logging var loggerFactory = new LoggerFactory(); - //loggerFactory.AddConsole(LogLevel.Debug); + //loggerFactory.AddConsole(LogLevel.Debug); - Subject = new WorkflowExecutor(Registry, ServiceProvider, ScopeProvider, DateTimeProvider, ResultProcesser, EventHub, CancellationProcessor, Options, loggerFactory); + Subject = new WorkflowExecutor(Registry, ServiceProvider, ScopeProvider, DateTimeProvider, ResultProcesser, EventHub, CancellationProcessor, Options, MiddlewareRunner, StepExecutor, loggerFactory); } [Fact(DisplayName = "Should execute active step")] public void should_execute_active_step() { - //arrange + //arrange var step1Body = A.Fake(); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); WorkflowStep step1 = BuildFakeStep(step1Body); @@ -72,7 +87,7 @@ public void should_execute_active_step() { new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } }) - }; + }; //act Subject.Execute(instance); @@ -85,7 +100,7 @@ public void should_execute_active_step() [Fact(DisplayName = "Should trigger step hooks")] public void should_trigger_step_hooks() { - //arrange + //arrange var step1Body = A.Fake(); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); WorkflowStep step1 = BuildFakeStep(step1Body); @@ -116,7 +131,7 @@ public void should_trigger_step_hooks() [Fact(DisplayName = "Should not execute inactive step")] public void should_not_execute_inactive_step() { - //arrange + //arrange var step1Body = A.Fake(); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); WorkflowStep step1 = BuildFakeStep(step1Body); @@ -134,7 +149,7 @@ public void should_not_execute_inactive_step() new ExecutionPointer() { Id = "1", Active = false, StepId = 0 } }) }; - + //act Subject.Execute(instance); @@ -148,7 +163,7 @@ public void should_map_inputs() //arrange var param = A.Fake(); - var step1Body = A.Fake(); + var step1Body = A.Fake(); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); WorkflowStep step1 = BuildFakeStep(step1Body, new List() { @@ -183,7 +198,7 @@ public void should_map_inputs() [Fact(DisplayName = "Should map outputs")] public void should_map_outputs() { - //arrange + //arrange var param = A.Fake(); var step1Body = A.Fake(); @@ -212,7 +227,7 @@ public void should_map_outputs() new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } }) }; - + //act Subject.Execute(instance); @@ -221,12 +236,12 @@ public void should_map_outputs() .MustHaveHappened(); } - + [Fact(DisplayName = "Should handle step exception")] public void should_handle_step_exception() { - //arrange + //arrange var step1Body = A.Fake(); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Throws(); WorkflowStep step1 = BuildFakeStep(step1Body); @@ -251,13 +266,13 @@ public void should_handle_step_exception() //assert A.CallTo(() => step1Body.RunAsync(A.Ignored)).MustHaveHappened(); A.CallTo(() => ResultProcesser.HandleStepException(instance, A.Ignored, A.Ignored, step1, A.Ignored)).MustHaveHappened(); - A.CallTo(() => ResultProcesser.ProcessExecutionResult(instance, A.Ignored, A.Ignored, step1, A.Ignored, A.Ignored)).MustNotHaveHappened(); + A.CallTo(() => ResultProcesser.ProcessExecutionResult(instance, A.Ignored, A.Ignored, step1, A.Ignored, A.Ignored)).MustNotHaveHappened(); } [Fact(DisplayName = "Should process after execution iteration")] public void should_process_after_execution_iteration() { - //arrange + //arrange var step1Body = A.Fake(); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Persist(null)); WorkflowStep step1 = BuildFakeStep(step1Body); @@ -286,7 +301,7 @@ public void should_process_after_execution_iteration() [Fact(DisplayName = "Should process cancellations")] public void should_process_cancellations() { - //arrange + //arrange var step1Body = A.Fake(); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Persist(null)); WorkflowStep step1 = BuildFakeStep(step1Body); diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs b/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs new file mode 100644 index 000000000..991acfb23 --- /dev/null +++ b/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; +using FakeItEasy; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Services; +using Xunit; +using Xunit.Abstractions; + +namespace WorkflowCore.UnitTests.Services +{ + public class WorkflowMiddlewareRunnerTests + { + protected List Middleware { get; } + protected WorkflowInstance Workflow { get; } + protected WorkflowDefinition Definition { get; } + protected IServiceProvider ServiceProvider { get; } + protected IWorkflowMiddlewareErrorHandler TopLevelErrorHandler { get; } + protected IDefLevelErrorHandler DefLevelErrorHandler { get; } + protected IWorkflowMiddlewareRunner Runner { get; } + protected ITestOutputHelper Out { get; } + + public WorkflowMiddlewareRunnerTests(ITestOutputHelper output) + { + Out = output; + Middleware = new List(); + Workflow = new WorkflowInstance(); + Definition = new WorkflowDefinition(); + TopLevelErrorHandler = A.Fake(); + DefLevelErrorHandler = A.Fake(); + ServiceProvider = new ServiceCollection() + .AddTransient(_ => TopLevelErrorHandler) + .AddTransient(_ => DefLevelErrorHandler) + .BuildServiceProvider(); + + A + .CallTo(HandleMethodFor(TopLevelErrorHandler)) + .Returns(Task.CompletedTask); + A + .CallTo(HandleMethodFor(DefLevelErrorHandler)) + .Returns(Task.CompletedTask); + + Runner = new WorkflowMiddlewareRunner(Middleware, ServiceProvider); + } + + + [Fact(DisplayName = "RunPreMiddleware should run nothing when no middleware")] + public void RunPreMiddleware_should_run_nothing_when_no_middleware() + { + // Act + Func action = async () => await Runner.RunPreMiddleware(Workflow, Definition); + + // Assert + action.ShouldNotThrow(); + } + + [Fact(DisplayName = "RunPreMiddleware should run middleware when one middleware")] + public async Task RunPreMiddleware_should_run_middleware_when_one_middleware() + { + // Arrange + var middleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow); + Middleware.Add(middleware); + + // Act + await Runner.RunPreMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(middleware)) + .MustHaveHappenedOnceExactly(); + } + + [Fact(DisplayName = "RunPreMiddleware should run all middleware when multiple middleware")] + public async Task RunPreMiddleware_should_run_all_middleware_when_multiple_middleware() + { + // Arrange + var middleware1 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow, 1); + var middleware2 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow, 2); + var middleware3 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow, 3); + Middleware.AddRange(new[] { middleware1, middleware2, middleware3 }); + + // Act + await Runner.RunPreMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(middleware3)) + .MustHaveHappenedOnceExactly() + .Then(A + .CallTo(HandleMethodFor(middleware2)) + .MustHaveHappenedOnceExactly()) + .Then(A + .CallTo(HandleMethodFor(middleware1)) + .MustHaveHappenedOnceExactly()); + } + + [Fact(DisplayName = "RunPreMiddleware should only run middleware in PreWorkflow phase")] + public async Task RunPreMiddleware_should_only_run_middleware_in_PreWorkflow_phase() + { + // Arrange + var preMiddleware1 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow, 1); + var preMiddleware2 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow, 2); + var postMiddleware1 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 3); + var postMiddleware2 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 4); + Middleware.AddRange(new[] { postMiddleware1, postMiddleware2, preMiddleware1, preMiddleware2 }); + + // Act + await Runner.RunPreMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(preMiddleware2)) + .MustHaveHappenedOnceExactly() + .Then(A + .CallTo(HandleMethodFor(preMiddleware1)) + .MustHaveHappenedOnceExactly()); + + A.CallTo(HandleMethodFor(postMiddleware1)).MustNotHaveHappened(); + A.CallTo(HandleMethodFor(postMiddleware2)).MustNotHaveHappened(); + } + + [Fact(DisplayName = "RunPostMiddleware should run nothing when no middleware")] + public void RunPostMiddleware_should_run_nothing_when_no_middleware() + { + // Act + Func action = async () => await Runner.RunPostMiddleware(Workflow, Definition); + + // Assert + action.ShouldNotThrow(); + } + + [Fact(DisplayName = "RunPostMiddleware should run middleware when one middleware")] + public async Task RunPostMiddleware_should_run_middleware_when_one_middleware() + { + // Arrange + var middleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow); + Middleware.Add(middleware); + + // Act + await Runner.RunPostMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(middleware)) + .MustHaveHappenedOnceExactly(); + } + + [Fact(DisplayName = "RunPostMiddleware should run all middleware when multiple middleware")] + public async Task RunPostMiddleware_should_run_all_middleware_when_multiple_middleware() + { + // Arrange + var middleware1 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 1); + var middleware2 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 2); + var middleware3 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 3); + Middleware.AddRange(new[] { middleware1, middleware2, middleware3 }); + + // Act + await Runner.RunPostMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(middleware3)) + .MustHaveHappenedOnceExactly() + .Then(A + .CallTo(HandleMethodFor(middleware2)) + .MustHaveHappenedOnceExactly()) + .Then(A + .CallTo(HandleMethodFor(middleware1)) + .MustHaveHappenedOnceExactly()); + } + + [Fact(DisplayName = "RunPostMiddleware should only run middleware in PostWorkflow phase")] + public async Task RunPostMiddleware_should_only_run_middleware_in_PostWorkflow_phase() + { + // Arrange + var postMiddleware1 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 1); + var postMiddleware2 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 2); + var preMiddleware1 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow, 3); + var preMiddleware2 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow, 4); + Middleware.AddRange(new[] { preMiddleware1, postMiddleware1, preMiddleware2, postMiddleware2 }); + + // Act + await Runner.RunPostMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(postMiddleware2)) + .MustHaveHappenedOnceExactly() + .Then(A + .CallTo(HandleMethodFor(postMiddleware1)) + .MustHaveHappenedOnceExactly()); + + A.CallTo(HandleMethodFor(preMiddleware1)).MustNotHaveHappened(); + A.CallTo(HandleMethodFor(preMiddleware1)).MustNotHaveHappened(); + } + + [Fact(DisplayName = "RunPostMiddleware should call top level error handler when middleware throws")] + public async Task RunPostMiddleware_should_call_top_level_error_handler_when_middleware_throws() + { + // Arrange + var middleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 1); + A.CallTo(HandleMethodFor(middleware)).ThrowsAsync(new ApplicationException("Something went wrong")); + Middleware.AddRange(new[] { middleware }); + + // Act + await Runner.RunPostMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(TopLevelErrorHandler)) + .MustHaveHappenedOnceExactly(); + } + + [Fact(DisplayName = + "RunPostMiddleware should call error handler on workflow def when middleware throws and def has handler defined")] + public async Task + RunPostMiddleware_should_call_error_handler_on_workflow_def_when_middleware_throws_and_def_has_handler() + { + // Arrange + var middleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 1); + A.CallTo(HandleMethodFor(middleware)).ThrowsAsync(new ApplicationException("Something went wrong")); + Middleware.AddRange(new[] { middleware }); + Definition.OnPostMiddlewareError = typeof(IDefLevelErrorHandler); + + // Act + await Runner.RunPostMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(TopLevelErrorHandler)) + .MustNotHaveHappened(); + A + .CallTo(HandleMethodFor(DefLevelErrorHandler)) + .MustHaveHappenedOnceExactly(); + } + + #region Helpers + + private IWorkflowMiddleware BuildWorkflowMiddleware( + WorkflowMiddlewarePhase phase, + int id = 0 + ) + { + var middleware = A.Fake(); + A.CallTo(() => middleware.Phase).Returns(phase); + A + .CallTo(HandleMethodFor(middleware)) + .ReturnsLazily(async call => + { + Out.WriteLine($@"Before workflow middleware {id}"); + await call.Arguments[1].As().Invoke(); + Out.WriteLine($@"After workflow middleware {id}"); + }); + + return middleware; + } + + private static Expression> HandleMethodFor(IWorkflowMiddleware middleware) => + () => middleware.HandleAsync( + A._, + A._); + + private static Expression> HandleMethodFor(IWorkflowMiddlewareErrorHandler errorHandler) => + () => errorHandler.HandleAsync(A._); + + public interface IDefLevelErrorHandler : IWorkflowMiddlewareErrorHandler + { + } + + #endregion + } +} From 60314a46f1a822cbe1e8ec1ea9322a9cd451d33f Mon Sep 17 00:00:00 2001 From: DanilF Date: Sun, 1 Nov 2020 21:11:19 -0500 Subject: [PATCH 242/462] Fix race condition in MiddlewareScenario --- .../Scenarios/MiddlewareScenario.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/MiddlewareScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/MiddlewareScenario.cs index 1c6929209..530b94a58 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/MiddlewareScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/MiddlewareScenario.cs @@ -136,6 +136,12 @@ public async Task Should_run_all_workflow_and_step_middleware() // Workflow should complete without errors status.Should().Be(WorkflowStatus.Complete); UnhandledStepErrors.Count.Should().Be(0); + + // Wait for post middleware to complete + while (_workflowMiddleware.Any(x => !x.HasCompleted)) + { + await Task.Delay(500); + } // Each middleware should have run _workflowMiddleware.Should() From 499abf928d45f02525cfe5fb444e2fd982183fae Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 2 Nov 2020 17:57:51 -0800 Subject: [PATCH 243/462] bump version, release notes --- ReleaseNotes/3.3.0.md | 284 +++++++++++++++++++ src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 2 +- src/WorkflowCore/WorkflowCore.csproj | 8 +- 3 files changed, 289 insertions(+), 5 deletions(-) create mode 100644 ReleaseNotes/3.3.0.md diff --git a/ReleaseNotes/3.3.0.md b/ReleaseNotes/3.3.0.md new file mode 100644 index 000000000..da825c7ee --- /dev/null +++ b/ReleaseNotes/3.3.0.md @@ -0,0 +1,284 @@ +# Workflow Core 3.3.0 + +# Workflow Middleware + +Workflows can be extended with Middleware that run before/after workflows start/complete as well as around workflow steps to provide flexibility in implementing cross-cutting concerns such as [log correlation](https://www.frakkingsweet.com/net-core-log-correlation-easy-access-to-headers/), [retries](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-http-call-retries-exponential-backoff-polly), and other use-cases. + +This is done by implementing and registering `IWorkflowMiddleware` for workflows or `IWorkflowStepMiddleware` for steps. + +## Step Middleware + +Step middleware lets you run additional code around the execution of a given step and alter its behavior. Implementing a step middleware should look familiar to anyone familiar with [ASP.NET Core's middleware pipeline](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1) or [`HttpClient`'s `DelegatingHandler` middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1#outgoing-request-middleware). + +### Usage + +First, create your own middleware class that implements `IWorkflowStepMiddleware`. Here's an example of a middleware that adds workflow ID and step ID to the log correlation context of every workflow step in your app. + +**Important:** You must make sure to call `next()` as part of your middleware. If you do not do this, your step will never run. + +```cs +public class LogCorrelationStepMiddleware : IWorkflowStepMiddleware +{ + private readonly ILogger _log; + + public LogCorrelationStepMiddleware( + ILogger log) + { + _log = log; + } + + public async Task HandleAsync( + IStepExecutionContext context, + IStepBody body, + WorkflowStepDelegate next) + { + var workflowId = context.Workflow.Id; + var stepId = context.Step.Id; + + // Uses log scope to add a few attributes to the scope + using (_log.BeginScope("{@WorkflowId}", workflowId)) + using (_log.BeginScope("{@StepId}", stepId)) + { + // Calling next ensures step gets executed + return await next(); + } + } +} +``` + +Here's another example of a middleware that uses the [Polly](https://github.com/App-vNext/Polly) dotnet resiliency library to implement retries on workflow steps based off a custom retry policy. + +```cs +public class PollyRetryStepMiddleware : IWorkflowStepMiddleware +{ + private const string StepContextKey = "WorkflowStepContext"; + private const int MaxRetries = 3; + private readonly ILogger _log; + + public PollyRetryMiddleware(ILogger log) + { + _log = log; + } + + // Consult Polly's docs for more information on how to build + // retry policies: + // https://github.com/App-vNext/Polly + public IAsyncPolicy GetRetryPolicy() => + Policy + .Handle() + .RetryAsync( + MaxRetries, + (result, retryCount, context) => + UpdateRetryCount( + result.Exception, + retryCount, + context[StepContextKey] as IStepExecutionContext) + ); + + public async Task HandleAsync( + IStepExecutionContext context, + IStepBody body, + WorkflowStepDelegate next + ) + { + return await GetRetryPolicy().ExecuteAsync( + ctx => next(), + // The step execution context gets passed down so that + // the step is accessible within the retry policy + new Dictionary + { + { StepContextKey, context } + }); + } + + private Task UpdateRetryCount( + Exception exception, + int retryCount, + IStepExecutionContext stepContext) + { + var stepInstance = stepContext.ExecutionPointer; + stepInstance.RetryCount = retryCount; + return Task.CompletedTask; + } +} +``` + +## Pre/Post Workflow Middleware + +Workflow middleware run either before a workflow starts or after a workflow completes and can be used to hook into the workflow lifecycle or alter the workflow itself before it is started. + +### Pre Workflow Middleware + +These middleware get run before the workflow is started and can potentially alter properties on the `WorkflowInstance`. + +The following example illustrates setting the `Description` property on the `WorkflowInstance` using a middleware that interprets the data on the passed workflow. This is useful in cases where you want the description of the workflow to be derived from the data passed to the workflow. + +Note that you use `WorkflowMiddlewarePhase.PreWorkflow` to specify that it runs before the workflow starts. + +**Important:** You should call `next` as part of the workflow middleware to ensure that the next workflow in the chain runs. + +```cs +// AddDescriptionWorkflowMiddleware.cs +public class AddDescriptionWorkflowMiddleware : IWorkflowMiddleware +{ + public WorkflowMiddlewarePhase Phase => + WorkflowMiddlewarePhase.PreWorkflow; + + public Task HandleAsync( + WorkflowInstance workflow, + WorkflowDelegate next + ) + { + if (workflow.Data is IDescriptiveWorkflowParams descriptiveParams) + { + workflow.Description = descriptiveParams.Description; + } + + return next(); + } +} + +// IDescriptiveWorkflowParams.cs +public interface IDescriptiveWorkflowParams +{ + string Description { get; } +} + +// MyWorkflowParams.cs +public MyWorkflowParams : IDescriptiveWorkflowParams +{ + public string Description => $"Run task '{TaskName}'"; + + public string TaskName { get; set; } +} +``` + +### Exception Handling in Pre Workflow Middleware + +Pre workflow middleware exception handling gets treated differently from post workflow middleware. Since the middleware runs before the workflow starts, any exceptions thrown within a pre workflow middleware will bubble up to the `StartWorkflow` method and it is up to the caller of `StartWorkflow` to handle the exception and act accordingly. + +```cs +public async Task MyMethodThatStartsAWorkflow() +{ + try + { + await host.StartWorkflow("HelloWorld", 1, null); + } + catch(Exception ex) + { + // Handle the exception appropriately + } +} +``` + +### Post Workflow Middleware + +These middleware get run after the workflow has completed and can be used to perform additional actions for all workflows in your app. + +The following example illustrates how you can use a post workflow middleware to print a summary of the workflow to console. + +Note that you use `WorkflowMiddlewarePhase.PostWorkflow` to specify that it runs after the workflow completes. + +**Important:** You should call `next` as part of the workflow middleware to ensure that the next workflow in the chain runs. + +```cs +public class PrintWorkflowSummaryMiddleware : IWorkflowMiddleware +{ + private readonly ILogger _log; + + public PrintWorkflowSummaryMiddleware( + ILogger log + ) + { + _log = log; + } + + public WorkflowMiddlewarePhase Phase => + WorkflowMiddlewarePhase.PostWorkflow; + + public Task HandleAsync( + WorkflowInstance workflow, + WorkflowDelegate next + ) + { + if (!workflow.CompleteTime.HasValue) + { + return next(); + } + + var duration = workflow.CompleteTime.Value - workflow.CreateTime; + _log.LogInformation($@"Workflow {workflow.Description} completed in {duration:g}"); + + foreach (var step in workflow.ExecutionPointers) + { + var stepName = step.StepName; + var stepDuration = (step.EndTime - step.StartTime) ?? TimeSpan.Zero; + _log.LogInformation($" - Step {stepName} completed in {stepDuration:g}"); + } + + return next(); + } +} +``` + +### Exception Handling in Post Workflow Middleware + +Post workflow middleware exception handling gets treated differently from pre workflow middleware. At the time that the workflow completes, your workflow has ran already so an uncaught exception would be difficult to act on. + +By default, if a workflow middleware throws an exception, it will be logged and the workflow will complete as normal. This behavior can be changed, however. + +To override the default post workflow error handling for all workflows in your app, just register a new `IWorkflowMiddlewareErrorHandler` in the dependency injection framework with your custom behavior as follows. + +```cs +// CustomMiddlewareErrorHandler.cs +public class CustomHandler : IWorkflowMiddlewareErrorHandler +{ + public Task HandleAsync(Exception ex) + { + // Handle your error asynchronously + } +} + +// Startup.cs +public void ConfigureServices(IServiceCollection services) +{ + // Other workflow configuration + services.AddWorkflow(); + + // Should go after .AddWorkflow() + services.AddTransient(); +} +``` + +## Registering Middleware + +In order for middleware to take effect, they must be registered with the built-in dependency injection framework using the convenience helpers. + +**Note:** Middleware will be run in the order that they are registered with middleware that are registered earlier running earlier in the chain and finishing later in the chain. For pre/post workflow middleware, all pre middleware will be run before a workflow starts and all post middleware will be run after a workflow completes. + +```cs +public class Startup +{ + public void ConfigureServices(IServiceCollection services) + { + ... + + // Add workflow middleware + services.AddWorkflowMiddleware(); + services.AddWorkflowMiddleware(); + + // Add step middleware + services.AddWorkflowStepMiddleware(); + services.AddWorkflowStepMiddleware(); + + ... + } +} +``` + +## More Information + +See the [Workflow Middleware](https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample19) sample for full examples of workflow middleware in action. + + +Many thanks to Danil Flores @dflor003 for this contribution! \ No newline at end of file diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index 913c643f4..3670d6553 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 3.1.5 + 3.3.0 DSL extenstion for Workflow Core provding support for JSON and YAML workflow definitions. Daniel Gerlag diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index c4ff1258a..3979b9139 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.2.6 - 3.2.6.0 - 3.2.6.0 + 3.3.0 + 3.3.0.0 + 3.3.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.2.6 + 3.3.0 From bf110e398d4c7fdeabbfbed7f6498080c0ea78fa Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 21 Nov 2020 08:10:08 -0800 Subject: [PATCH 244/462] issue 605 --- src/WorkflowCore/Services/WorkflowExecutor.cs | 23 ++++++++----------- src/WorkflowCore/WorkflowCore.csproj | 8 +++---- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index a70de87a2..b0987c27d 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -229,22 +229,19 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo workflow.NextExecution = Math.Min(pointerSleep, workflow.NextExecution ?? pointerSleep); } - if (workflow.NextExecution == null) + foreach (var pointer in workflow.ExecutionPointers.Where(x => x.Active && (x.Children ?? new List()).Count > 0)) { - foreach (var pointer in workflow.ExecutionPointers.Where(x => x.Active && (x.Children ?? new List()).Count > 0)) - { - if (!workflow.ExecutionPointers.FindByScope(pointer.Id).All(x => x.EndTime.HasValue)) - continue; - - if (!pointer.SleepUntil.HasValue) - { - workflow.NextExecution = 0; - return; - } + if (!workflow.ExecutionPointers.FindByScope(pointer.Id).All(x => x.EndTime.HasValue)) + continue; - var pointerSleep = pointer.SleepUntil.Value.ToUniversalTime().Ticks; - workflow.NextExecution = Math.Min(pointerSleep, workflow.NextExecution ?? pointerSleep); + if (!pointer.SleepUntil.HasValue) + { + workflow.NextExecution = 0; + return; } + + var pointerSleep = pointer.SleepUntil.Value.ToUniversalTime().Ticks; + workflow.NextExecution = Math.Min(pointerSleep, workflow.NextExecution ?? pointerSleep); } if ((workflow.NextExecution != null) || (workflow.ExecutionPointers.Any(x => x.EndTime == null))) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 3979b9139..8b9f2a080 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.0 - 3.3.0.0 - 3.3.0.0 + 3.3.1 + 3.3.1.0 + 3.3.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.0 + 3.3.1 From 687bf2350b1fe02920b856de009145fc61933534 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 22 Nov 2020 13:32:51 -0800 Subject: [PATCH 245/462] ability to schedule execution pointers dependent another pointer completing first --- WorkflowCore.sln | 5 +++-- src/WorkflowCore/Models/ExecutionPointer.cs | 3 ++- .../Models/ExecutionPointerCollection.cs | 6 ++++++ .../Services/ErrorHandlers/CompensateHandler.cs | 16 +++++++++++++--- .../Services/ExecutionResultProcessor.cs | 10 ++++++++++ src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- .../Scenarios/MultistepCompensationScenario.cs | 4 ++-- 7 files changed, 40 insertions(+), 12 deletions(-) diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 09cda3de8..1ef2a187f 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -103,6 +103,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\2.1.2.md = ReleaseNotes\2.1.2.md ReleaseNotes\3.0.0.md = ReleaseNotes\3.0.0.md ReleaseNotes\3.1.0.md = ReleaseNotes\3.1.0.md + ReleaseNotes\3.3.0.md = ReleaseNotes\3.3.0.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" @@ -145,9 +146,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample09s", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchPad\ScratchPad.csproj", "{51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.QueueProviders.RabbitMQ", "test\WorkflowCore.Tests.QueueProviders.RabbitMQ\WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj", "{54DE20BA-EBA7-4BF0-9BD9-F03766849716}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.QueueProviders.RabbitMQ", "test\WorkflowCore.Tests.QueueProviders.RabbitMQ\WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj", "{54DE20BA-EBA7-4BF0-9BD9-F03766849716}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample19", "src\samples\WorkflowCore.Sample19\WorkflowCore.Sample19.csproj", "{1223ED47-3E5E-4960-B70D-DFAF550F6666}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample19", "src\samples\WorkflowCore.Sample19\WorkflowCore.Sample19.csproj", "{1223ED47-3E5E-4960-B70D-DFAF550F6666}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/WorkflowCore/Models/ExecutionPointer.cs b/src/WorkflowCore/Models/ExecutionPointer.cs index 53938d76a..6c12afda7 100644 --- a/src/WorkflowCore/Models/ExecutionPointer.cs +++ b/src/WorkflowCore/Models/ExecutionPointer.cs @@ -63,6 +63,7 @@ public enum PointerStatus WaitingForEvent = 5, Failed = 6, Compensated = 7, - Cancelled = 8 + Cancelled = 8, + PendingPredecessor = 9 } } diff --git a/src/WorkflowCore/Models/ExecutionPointerCollection.cs b/src/WorkflowCore/Models/ExecutionPointerCollection.cs index a81e9fcf8..90e64348c 100644 --- a/src/WorkflowCore/Models/ExecutionPointerCollection.cs +++ b/src/WorkflowCore/Models/ExecutionPointerCollection.cs @@ -97,6 +97,12 @@ public ExecutionPointer Find(Predicate match) return _dictionary.Values.FirstOrDefault(x => match(x)); } + public ICollection FindByStatus(PointerStatus status) + { + //TODO: track states in hash table + return _dictionary.Values.Where(x => x.Status == status).ToList(); + } + public int Count => _dictionary.Count; public bool IsReadOnly => false; } diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index 12b62342a..2023a59f1 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -63,11 +63,13 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP scopePointer.EndTime = _datetimeProvider.UtcNow; scopePointer.Status = PointerStatus.Failed; + ExecutionPointer compensationPointer = null; + if (scopeStep.CompensationStepId.HasValue) { scopePointer.Status = PointerStatus.Compensated; - var compensationPointer = _pointerFactory.BuildCompensationPointer(def, scopePointer, exceptionPointer, scopeStep.CompensationStepId.Value); + compensationPointer = _pointerFactory.BuildCompensationPointer(def, scopePointer, exceptionPointer, scopeStep.CompensationStepId.Value); workflow.ExecutionPointers.Add(compensationPointer); if (resume) @@ -89,8 +91,16 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP var siblingStep = def.Steps.FindById(siblingPointer.StepId); if (siblingStep.CompensationStepId.HasValue) { - var compensationPointer = _pointerFactory.BuildCompensationPointer(def, siblingPointer, exceptionPointer, siblingStep.CompensationStepId.Value); - workflow.ExecutionPointers.Add(compensationPointer); + var nextCompensationPointer = _pointerFactory.BuildCompensationPointer(def, siblingPointer, exceptionPointer, siblingStep.CompensationStepId.Value); + if (compensationPointer != null) + { + nextCompensationPointer.Active = false; + nextCompensationPointer.Status = PointerStatus.PendingPredecessor; + nextCompensationPointer.PredecessorId = compensationPointer.Id; + compensationPointer = nextCompensationPointer; + } + workflow.ExecutionPointers.Add(nextCompensationPointer); + siblingPointer.Status = PointerStatus.Compensated; } } diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index 448506823..781f3d39d 100755 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -67,6 +67,16 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, pointer, outcomeTarget)); } + var pendingSubsequents = workflow.ExecutionPointers + .FindByStatus(PointerStatus.PendingPredecessor) + .Where(x => x.PredecessorId == pointer.Id); + + foreach (var subsequent in pendingSubsequents) + { + subsequent.Status = PointerStatus.Pending; + subsequent.Active = true; + } + _eventPublisher.PublishNotification(new StepCompleted() { EventTimeUtc = _datetimeProvider.UtcNow, diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 8b9f2a080..53cd19bcd 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.1 - 3.3.1.0 - 3.3.1.0 + 3.3.2 + 3.3.2.0 + 3.3.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.1 + 3.3.2 diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs index 68fedfae9..afdbe588a 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs @@ -34,11 +34,11 @@ public void Build(IWorkflowBuilder builder) Compensation1Fired = CompensationCounter; }) .Then(context => ExecutionResult.Next()) - .CompensateWith(context => + .CompensateWithSequence(context => context.StartWith(c => { CompensationCounter++; Compensation2Fired = CompensationCounter; - }) + })) .Then(context => ExecutionResult.Next()) .CompensateWith(context => { From 8593a5425dfe266dab46f88c6960a247fc49069e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=88=91=E9=9B=B60=E4=B8=83?= Date: Tue, 24 Nov 2020 15:15:37 +0800 Subject: [PATCH 246/462] add support for StepBodyAsync --- src/WorkflowCore.DSL/Services/DefinitionLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index 27d84492b..6acd6e33b 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -72,7 +72,7 @@ private WorkflowStepCollection ConvertSteps(ICollection source, Ty WorkflowStep targetStep; Type containerType; - if (stepType.IsSubclassOf(typeof(StepBody))) + if (stepType.GetInterfaces().Contains(typeof(IStepBody))) { containerType = typeof(WorkflowStep<>).MakeGenericType(stepType); From b1a0f42da4e7ff6894425de2acbd9e2fd6d9f299 Mon Sep 17 00:00:00 2001 From: Stephen Brown Date: Wed, 25 Nov 2020 18:00:15 +0100 Subject: [PATCH 247/462] Update to Entity Framework 5 --- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- .../WorkflowCore.Users/WorkflowCore.Users.csproj | 2 +- .../WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj | 2 +- ...WorkflowCore.Persistence.EntityFramework.csproj | 6 +++--- .../WorkflowCore.Persistence.MySQL.csproj | 6 +++--- .../WorkflowCore.Persistence.PostgreSQL.csproj | 8 ++++---- .../SqlServerContext.cs | 12 ++++++------ .../WorkflowCore.Persistence.SqlServer.csproj | 8 ++++---- .../WorkflowCore.Persistence.Sqlite.csproj | 4 ++-- .../WorkflowCore.Providers.AWS.csproj | 2 +- .../WorkflowCore.Providers.Elasticsearch.csproj | 2 +- .../WorkflowCore.Providers.Redis.csproj | 2 +- .../WorkflowCore.QueueProviders.SqlServer.csproj | 2 +- .../WorkflowCore.Sample01.csproj | 14 +++++++------- .../WorkflowCore.Sample02.csproj | 6 +++--- .../WorkflowCore.Sample03.csproj | 8 ++++---- .../WorkflowCore.Sample04.csproj | 8 ++++---- .../WorkflowCore.Sample05.csproj | 2 +- .../WorkflowCore.Sample06.csproj | 2 +- .../WorkflowCore.Sample07.csproj | 4 ++-- .../WorkflowCore.Sample08.csproj | 4 ++-- .../WorkflowCore.Sample09.csproj | 4 ++-- .../WorkflowCore.Sample09s.csproj | 6 +++--- .../WorkflowCore.Sample10.csproj | 4 ++-- .../WorkflowCore.Sample11.csproj | 6 +++--- .../WorkflowCore.Sample12.csproj | 4 ++-- .../WorkflowCore.Sample13.csproj | 4 ++-- .../WorkflowCore.Sample14.csproj | 2 +- .../WorkflowCore.Sample15.csproj | 6 +++--- .../WorkflowCore.Sample16.csproj | 6 +++--- .../WorkflowCore.Sample17.csproj | 4 ++-- .../WorkflowCore.Sample18.csproj | 12 ++++++------ .../WorkflowCore.Sample19.csproj | 12 ++++++------ .../WorkflowCore.TestSample01.csproj | 4 ++-- test/ScratchPad/ScratchPad.csproj | 6 +++--- .../WorkflowCore.IntegrationTests.csproj | 8 ++++---- .../WorkflowCore.TestAssets.csproj | 2 +- .../WorkflowCore.Testing.csproj | 6 +++--- .../WorkflowCore.Tests.DynamoDB.csproj | 2 +- .../WorkflowCore.Tests.Elasticsearch.csproj | 2 +- .../WorkflowCore.Tests.MongoDB.csproj | 8 ++++---- .../WorkflowCore.Tests.MySQL.csproj | 4 ++-- .../WorkflowCore.Tests.PostgreSQL.csproj | 4 ++-- .../WorkflowCore.Tests.Redis.csproj | 4 ++-- .../WorkflowCore.Tests.SqlServer.csproj | 2 +- .../WorkflowCore.Tests.Sqlite.csproj | 2 +- .../WorkflowCore.UnitTests.csproj | 8 ++++---- 47 files changed, 122 insertions(+), 122 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 53cd19bcd..0181a7bb7 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -25,10 +25,10 @@ - - - - + + + + diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 5a8c0beab..dc124dcbe 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj index a3fb9bbb7..a8cb28cbd 100644 --- a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj +++ b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index ed8392a62..712d77b86 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -3,7 +3,7 @@ Workflow Core EntityFramework Core Persistence Provider Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.EntityFramework WorkflowCore.Persistence.EntityFramework workflow;.NET;Core;state machine;WorkflowCore;EntityFramework;EntityFrameworkCore @@ -26,8 +26,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index a031e5fba..0a8309e94 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -4,7 +4,7 @@ Workflow Core MySQL Persistence Provider 1.0.0 Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.MySQL WorkflowCore.Persistence.MySQL workflow;.NET;Core;state machine;WorkflowCore;MySQL @@ -22,11 +22,11 @@ - + all runtime; build; native; contentfiles; analyzers - + diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 7addc52a4..ade140ddd 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -27,12 +27,12 @@ - - - + + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs index 499c10295..7383cce3e 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs @@ -28,37 +28,37 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void ConfigureSubscriptionStorage(EntityTypeBuilder builder) { builder.ToTable("Subscription", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureWorkflowStorage(EntityTypeBuilder builder) { builder.ToTable("Workflow", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureExecutionPointerStorage(EntityTypeBuilder builder) { builder.ToTable("ExecutionPointer", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureExecutionErrorStorage(EntityTypeBuilder builder) { builder.ToTable("ExecutionError", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureExetensionAttributeStorage(EntityTypeBuilder builder) { builder.ToTable("ExtensionAttribute", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureEventStorage(EntityTypeBuilder builder) { builder.ToTable("Event", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index be52279d1..f08ffcdfd 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -4,7 +4,7 @@ Workflow Core SQL Server Persistence Provider 1.8.0 Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.SqlServer WorkflowCore.Persistence.SqlServer workflow;.NET;Core;state machine;WorkflowCore @@ -28,11 +28,11 @@ - - + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 2d2789021..6ad612e7a 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -4,7 +4,7 @@ Workflow Core Sqlite Persistence Provider 1.5.0 Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.Sqlite WorkflowCore.Persistence.Sqlite workflow;.NET;Core;state machine;WorkflowCore;Sqlite @@ -28,7 +28,7 @@ - + diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 84eb6cb74..2be8582b5 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index e4263abaf..29e44395b 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index bdb8c6456..697d62fb3 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 36d7cda77..5abaea1a7 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj index e4a09e491..5f4857cca 100644 --- a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj +++ b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.Sample01 Exe WorkflowCore.Sample01 @@ -16,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj index 0765d50c3..7fab128b6 100644 --- a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj +++ b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.Sample02 Exe WorkflowCore.Sample02 @@ -11,8 +11,8 @@ - - + + diff --git a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj index fc4a5d4fd..7b332dcd9 100644 --- a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj +++ b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp3.1 WorkflowCore.Sample03 Exe WorkflowCore.Sample03 @@ -16,9 +16,9 @@ - - - + + + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index 82107b0e6..af4dd01f9 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 WorkflowCore.Sample04 Exe WorkflowCore.Sample04 @@ -29,9 +29,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj index 592fa228b..069931ef0 100644 --- a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj +++ b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj index b61478b9e..d4c71d9aa 100644 --- a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj +++ b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index 5ca51a866..2f5937f36 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 true WorkflowCore.Sample07 Exe @@ -26,7 +26,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj index 3ff429e28..2a556e45d 100644 --- a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj +++ b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 WorkflowCore.Sample08 Exe WorkflowCore.Sample08 @@ -21,7 +21,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj index 6f75a0d7d..bc16eb974 100644 --- a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj +++ b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj index e569ec895..bc16eb974 100644 --- a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj +++ b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj @@ -1,12 +1,12 @@ - + Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj index 6f75a0d7d..bc16eb974 100644 --- a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj +++ b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj index b6454f6a2..4eb65c992 100644 --- a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj +++ b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp2.2 + netcoreapp3.1 - - + + diff --git a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj index 9879baab2..5b030fb75 100644 --- a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj +++ b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj index dbe49166e..7f814f488 100644 --- a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj +++ b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp2.2 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj index fef8173cc..974472a25 100644 --- a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj +++ b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.1 diff --git a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj index c63fbd792..9fd7656d7 100644 --- a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj +++ b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp2.2 + netcoreapp3.1 - - + + diff --git a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj index c63fbd792..9fd7656d7 100644 --- a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj +++ b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp2.2 + netcoreapp3.1 - - + + diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index 26933096d..0c1689366 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj index 660b47511..46ad70bf5 100644 --- a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj +++ b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj @@ -1,15 +1,15 @@ - + Exe - netcoreapp3.0 + netcoreapp3.1 - - - - + + + + diff --git a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj index b2a7796c3..f1fbd9c5c 100644 --- a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj +++ b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj @@ -1,15 +1,15 @@ - + Exe - netcoreapp3.0 + netcoreapp3.1 - - - - + + + + diff --git a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj index 282fdbb15..789e6e445 100644 --- a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj +++ b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj @@ -1,12 +1,12 @@  - netcoreapp2.2 + netcoreapp3.1 - + diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 4fb61dd24..27c262350 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 ScratchPad Exe ScratchPad @@ -11,8 +11,8 @@ - - + + diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index 971f10c57..8739a6c6d 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.IntegrationTests WorkflowCore.IntegrationTests true @@ -21,10 +21,10 @@ - - + + - + diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index 12da1f57b..aeeb3d614 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.1 WorkflowCore.TestAssets WorkflowCore.TestAssets false diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 32f9c1363..8797551c8 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index c2563b8b2..f275a05cf 100644 --- a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -1,7 +1,7 @@ - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 168be514f..0ed01e311 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index bec4d8561..cb4beac90 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.Tests.MongoDB WorkflowCore.Tests.MongoDB true @@ -22,9 +22,9 @@ - - - + + + diff --git a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj index a255e01d6..66a792fe5 100644 --- a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj +++ b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj @@ -1,7 +1,7 @@ - + - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 1cc7d56bd..2fefbdea8 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 WorkflowCore.Tests.PostgreSQL WorkflowCore.Tests.PostgreSQL true @@ -21,7 +21,7 @@ - + diff --git a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj index c5ee9a6f8..76857bc6d 100644 --- a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj +++ b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj @@ -1,7 +1,7 @@ - + - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj index 011714983..497293aaf 100644 --- a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj +++ b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 diff --git a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj index 9ce1b77cd..9598398f4 100644 --- a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj +++ b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index 294adbd23..bc8321631 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.UnitTests WorkflowCore.UnitTests true @@ -20,9 +20,9 @@ - - - + + + From f22ef4e36546f3dec5de7a62ca97a9ea8d66c0ae Mon Sep 17 00:00:00 2001 From: Stephen Brown Date: Tue, 1 Dec 2020 18:11:11 +0100 Subject: [PATCH 248/462] Increase minor versions for all dotnet5 affected packages, sync up EF related packages with the main version number --- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- .../WorkflowCore.Users/WorkflowCore.Users.csproj | 6 +++--- .../WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj | 2 +- .../WorkflowCore.Persistence.EntityFramework.csproj | 8 ++++---- .../WorkflowCore.Persistence.MySQL.csproj | 6 +++--- .../WorkflowCore.Persistence.PostgreSQL.csproj | 8 ++++---- .../WorkflowCore.Persistence.SqlServer.csproj | 8 ++++---- .../WorkflowCore.Persistence.Sqlite.csproj | 8 ++++---- .../WorkflowCore.Providers.AWS.csproj | 6 +++--- .../WorkflowCore.Providers.Elasticsearch.csproj | 2 +- .../WorkflowCore.Providers.Redis.csproj | 4 ++-- .../WorkflowCore.QueueProviders.SqlServer.csproj | 2 +- test/WorkflowCore.Testing/WorkflowCore.Testing.csproj | 6 +++--- 13 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 0181a7bb7..1da781981 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.2 - 3.3.2.0 - 3.3.2.0 + 3.3.3 + 3.3.3.0 + 3.3.3.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.2 + 3.3.3 diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index dc124dcbe..fb3a51fc3 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -15,9 +15,9 @@ false false Provides extensions for Workflow Core to enable human workflows. - 2.1.1 - 2.1.1.0 - 2.1.1.0 + 2.1.2 + 2.1.2.0 + 2.1.2.0 diff --git a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj index a8cb28cbd..dbdec8e16 100644 --- a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj +++ b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj @@ -14,7 +14,7 @@ false false false - 2.0.0 + 2.0.1 WebAPI wrapper for Workflow host diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 712d77b86..214787365 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -14,11 +14,11 @@ false false false - 3.1.0 + 3.3.3 Base package for Workflow-core peristence providers using entity framework - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.3.3.0 + 3.3.3.0 + 3.3.3 diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index 0a8309e94..f1b09d045 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -16,9 +16,9 @@ false false Provides support to persist workflows running on Workflow Core to a MySQL database. - 3.0.1 - 3.0.1.0 - 3.0.1.0 + 3.3.3 + 3.3.3.0 + 3.3.3.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index ade140ddd..a041d19b1 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,10 +15,10 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 3.1.0 - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.3.3 + 3.3.3.0 + 3.3.3.0 + 3.3.3 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index f08ffcdfd..427bbbdf3 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,11 +15,11 @@ false false false - 3.1.0 + 3.3.3 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.3.3.0 + 3.3.3.0 + 3.3.3 diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 6ad612e7a..ddcc8deac 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,10 +16,10 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 3.1.0 - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.3.3 + 3.3.3.0 + 3.3.3.0 + 3.3.3 diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 2be8582b5..b1da0b2fc 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,9 +11,9 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 3.0.2 - 3.0.2.0 - 3.0.2 + 3.0.3 + 3.0.3.0 + 3.0.3 diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index 29e44395b..cf00e7d63 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 3.0.0 + 3.0.1 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index 697d62fb3..531435160 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,14 +2,14 @@ netstandard2.0 - 3.0.2 + 3.0.3 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git https://github.com/danielgerlag/workflow-core Redis providers for Workflow Core (Persistence, queueing, distributed locking and event hubs) - 3.0.2 + 3.0.3 diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 5abaea1a7..97b48d220 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -5,7 +5,7 @@ Roberto Paterlini Queue provider for Workflow-core using SQL Server Service Broker - 1.0.3-alpha + 1.0.4-alpha diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 8797551c8..981d713c4 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 2.3.0 - 2.3.0.0 - 2.3.0.0 + 2.3.1 + 2.3.1.0 + 2.3.1.0 Facilitates testing of workflows built on Workflow-Core From a96d7f618aac823b99a094aaf37102827449d187 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 2 Dec 2020 21:57:14 -0800 Subject: [PATCH 249/462] Revert "Update to Entity Framework 5" --- src/WorkflowCore/WorkflowCore.csproj | 16 ++++++++-------- .../WorkflowCore.Users/WorkflowCore.Users.csproj | 8 ++++---- .../WorkflowCore.WebAPI.csproj | 4 ++-- ...rkflowCore.Persistence.EntityFramework.csproj | 14 +++++++------- .../WorkflowCore.Persistence.MySQL.csproj | 12 ++++++------ .../WorkflowCore.Persistence.PostgreSQL.csproj | 16 ++++++++-------- .../SqlServerContext.cs | 12 ++++++------ .../WorkflowCore.Persistence.SqlServer.csproj | 16 ++++++++-------- .../WorkflowCore.Persistence.Sqlite.csproj | 12 ++++++------ .../WorkflowCore.Providers.AWS.csproj | 8 ++++---- .../WorkflowCore.Providers.Elasticsearch.csproj | 4 ++-- .../WorkflowCore.Providers.Redis.csproj | 6 +++--- .../WorkflowCore.QueueProviders.SqlServer.csproj | 4 ++-- .../WorkflowCore.Sample01.csproj | 14 +++++++------- .../WorkflowCore.Sample02.csproj | 6 +++--- .../WorkflowCore.Sample03.csproj | 8 ++++---- .../WorkflowCore.Sample04.csproj | 8 ++++---- .../WorkflowCore.Sample05.csproj | 2 +- .../WorkflowCore.Sample06.csproj | 2 +- .../WorkflowCore.Sample07.csproj | 4 ++-- .../WorkflowCore.Sample08.csproj | 4 ++-- .../WorkflowCore.Sample09.csproj | 4 ++-- .../WorkflowCore.Sample09s.csproj | 6 +++--- .../WorkflowCore.Sample10.csproj | 4 ++-- .../WorkflowCore.Sample11.csproj | 6 +++--- .../WorkflowCore.Sample12.csproj | 4 ++-- .../WorkflowCore.Sample13.csproj | 4 ++-- .../WorkflowCore.Sample14.csproj | 2 +- .../WorkflowCore.Sample15.csproj | 6 +++--- .../WorkflowCore.Sample16.csproj | 6 +++--- .../WorkflowCore.Sample17.csproj | 4 ++-- .../WorkflowCore.Sample18.csproj | 12 ++++++------ .../WorkflowCore.Sample19.csproj | 12 ++++++------ .../WorkflowCore.TestSample01.csproj | 4 ++-- test/ScratchPad/ScratchPad.csproj | 6 +++--- .../WorkflowCore.IntegrationTests.csproj | 8 ++++---- .../WorkflowCore.TestAssets.csproj | 2 +- .../WorkflowCore.Testing.csproj | 12 ++++++------ .../WorkflowCore.Tests.DynamoDB.csproj | 2 +- .../WorkflowCore.Tests.Elasticsearch.csproj | 2 +- .../WorkflowCore.Tests.MongoDB.csproj | 8 ++++---- .../WorkflowCore.Tests.MySQL.csproj | 4 ++-- .../WorkflowCore.Tests.PostgreSQL.csproj | 4 ++-- .../WorkflowCore.Tests.Redis.csproj | 4 ++-- .../WorkflowCore.Tests.SqlServer.csproj | 2 +- .../WorkflowCore.Tests.Sqlite.csproj | 2 +- .../WorkflowCore.UnitTests.csproj | 8 ++++---- 47 files changed, 159 insertions(+), 159 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 1da781981..53cd19bcd 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,20 +15,20 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.3 - 3.3.3.0 - 3.3.3.0 + 3.3.2 + 3.3.2.0 + 3.3.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.3 + 3.3.2 - - - - + + + + diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index fb3a51fc3..5a8c0beab 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -15,9 +15,9 @@ false false Provides extensions for Workflow Core to enable human workflows. - 2.1.2 - 2.1.2.0 - 2.1.2.0 + 2.1.1 + 2.1.1.0 + 2.1.1.0 @@ -25,7 +25,7 @@ - + diff --git a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj index dbdec8e16..a3fb9bbb7 100644 --- a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj +++ b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj @@ -14,7 +14,7 @@ false false false - 2.0.1 + 2.0.0 WebAPI wrapper for Workflow host @@ -25,7 +25,7 @@ - + diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 214787365..ed8392a62 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -3,7 +3,7 @@ Workflow Core EntityFramework Core Persistence Provider Daniel Gerlag - netstandard2.1 + netstandard2.0 WorkflowCore.Persistence.EntityFramework WorkflowCore.Persistence.EntityFramework workflow;.NET;Core;state machine;WorkflowCore;EntityFramework;EntityFrameworkCore @@ -14,11 +14,11 @@ false false false - 3.3.3 + 3.1.0 Base package for Workflow-core peristence providers using entity framework - 3.3.3.0 - 3.3.3.0 - 3.3.3 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -26,8 +26,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index f1b09d045..a031e5fba 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -4,7 +4,7 @@ Workflow Core MySQL Persistence Provider 1.0.0 Daniel Gerlag - netstandard2.1 + netstandard2.0 WorkflowCore.Persistence.MySQL WorkflowCore.Persistence.MySQL workflow;.NET;Core;state machine;WorkflowCore;MySQL @@ -16,17 +16,17 @@ false false Provides support to persist workflows running on Workflow Core to a MySQL database. - 3.3.3 - 3.3.3.0 - 3.3.3.0 + 3.0.1 + 3.0.1.0 + 3.0.1.0 - + all runtime; build; native; contentfiles; analyzers - + diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index a041d19b1..7addc52a4 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,10 +15,10 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 3.3.3 - 3.3.3.0 - 3.3.3.0 - 3.3.3 + 3.1.0 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -27,12 +27,12 @@ - - - + + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs index 7383cce3e..499c10295 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs @@ -28,37 +28,37 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void ConfigureSubscriptionStorage(EntityTypeBuilder builder) { builder.ToTable("Subscription", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } protected override void ConfigureWorkflowStorage(EntityTypeBuilder builder) { builder.ToTable("Workflow", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } protected override void ConfigureExecutionPointerStorage(EntityTypeBuilder builder) { builder.ToTable("ExecutionPointer", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } protected override void ConfigureExecutionErrorStorage(EntityTypeBuilder builder) { builder.ToTable("ExecutionError", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } protected override void ConfigureExetensionAttributeStorage(EntityTypeBuilder builder) { builder.ToTable("ExtensionAttribute", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } protected override void ConfigureEventStorage(EntityTypeBuilder builder) { builder.ToTable("Event", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 427bbbdf3..be52279d1 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -4,7 +4,7 @@ Workflow Core SQL Server Persistence Provider 1.8.0 Daniel Gerlag - netstandard2.1 + netstandard2.0 WorkflowCore.Persistence.SqlServer WorkflowCore.Persistence.SqlServer workflow;.NET;Core;state machine;WorkflowCore @@ -15,11 +15,11 @@ false false false - 3.3.3 + 3.1.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 3.3.3.0 - 3.3.3.0 - 3.3.3 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -28,11 +28,11 @@ - - + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index ddcc8deac..2d2789021 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -4,7 +4,7 @@ Workflow Core Sqlite Persistence Provider 1.5.0 Daniel Gerlag - netstandard2.1 + netstandard2.0 WorkflowCore.Persistence.Sqlite WorkflowCore.Persistence.Sqlite workflow;.NET;Core;state machine;WorkflowCore;Sqlite @@ -16,10 +16,10 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 3.3.3 - 3.3.3.0 - 3.3.3.0 - 3.3.3 + 3.1.0 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -28,7 +28,7 @@ - + diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index b1da0b2fc..84eb6cb74 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,16 +11,16 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 3.0.3 - 3.0.3.0 - 3.0.3 + 3.0.2 + 3.0.2.0 + 3.0.2 - + diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index cf00e7d63..e4263abaf 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 3.0.1 + 3.0.0 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git @@ -13,7 +13,7 @@ - + diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index 531435160..bdb8c6456 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,18 +2,18 @@ netstandard2.0 - 3.0.3 + 3.0.2 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git https://github.com/danielgerlag/workflow-core Redis providers for Workflow Core (Persistence, queueing, distributed locking and event hubs) - 3.0.3 + 3.0.2 - + diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 97b48d220..36d7cda77 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -5,7 +5,7 @@ Roberto Paterlini Queue provider for Workflow-core using SQL Server Service Broker - 1.0.4-alpha + 1.0.3-alpha @@ -20,7 +20,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj index 5f4857cca..e4a09e491 100644 --- a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj +++ b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 WorkflowCore.Sample01 Exe WorkflowCore.Sample01 @@ -16,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj index 7fab128b6..0765d50c3 100644 --- a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj +++ b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 WorkflowCore.Sample02 Exe WorkflowCore.Sample02 @@ -11,8 +11,8 @@ - - + + diff --git a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj index 7b332dcd9..fc4a5d4fd 100644 --- a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj +++ b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.0 WorkflowCore.Sample03 Exe WorkflowCore.Sample03 @@ -16,9 +16,9 @@ - - - + + + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index af4dd01f9..82107b0e6 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0 WorkflowCore.Sample04 Exe WorkflowCore.Sample04 @@ -29,9 +29,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj index 069931ef0..592fa228b 100644 --- a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj +++ b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj index d4c71d9aa..b61478b9e 100644 --- a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj +++ b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index 2f5937f36..5ca51a866 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0 true WorkflowCore.Sample07 Exe @@ -26,7 +26,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj index 2a556e45d..3ff429e28 100644 --- a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj +++ b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0 WorkflowCore.Sample08 Exe WorkflowCore.Sample08 @@ -21,7 +21,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj index bc16eb974..6f75a0d7d 100644 --- a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj +++ b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + netcoreapp3.0 - + diff --git a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj index bc16eb974..e569ec895 100644 --- a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj +++ b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj @@ -1,12 +1,12 @@ - + Exe - netcoreapp3.1 + netcoreapp3.0 - + diff --git a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj index bc16eb974..6f75a0d7d 100644 --- a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj +++ b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + netcoreapp3.0 - + diff --git a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj index 4eb65c992..b6454f6a2 100644 --- a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj +++ b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp3.1 + netcoreapp2.2 - - + + diff --git a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj index 5b030fb75..9879baab2 100644 --- a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj +++ b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + netcoreapp3.0 - + diff --git a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj index 7f814f488..dbe49166e 100644 --- a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj +++ b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + netcoreapp2.2 - + diff --git a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj index 974472a25..fef8173cc 100644 --- a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj +++ b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + netcoreapp2.2 diff --git a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj index 9fd7656d7..c63fbd792 100644 --- a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj +++ b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp3.1 + netcoreapp2.2 - - + + diff --git a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj index 9fd7656d7..c63fbd792 100644 --- a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj +++ b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp3.1 + netcoreapp2.2 - - + + diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index 0c1689366..26933096d 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + netcoreapp3.0 - + diff --git a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj index 46ad70bf5..660b47511 100644 --- a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj +++ b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj @@ -1,15 +1,15 @@ - + Exe - netcoreapp3.1 + netcoreapp3.0 - - - - + + + + diff --git a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj index f1fbd9c5c..b2a7796c3 100644 --- a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj +++ b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj @@ -1,15 +1,15 @@ - + Exe - netcoreapp3.1 + netcoreapp3.0 - - - - + + + + diff --git a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj index 789e6e445..282fdbb15 100644 --- a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj +++ b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj @@ -1,12 +1,12 @@  - netcoreapp3.1 + netcoreapp2.2 - + diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 27c262350..4fb61dd24 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0 ScratchPad Exe ScratchPad @@ -11,8 +11,8 @@ - - + + diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index 8739a6c6d..971f10c57 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 WorkflowCore.IntegrationTests WorkflowCore.IntegrationTests true @@ -21,10 +21,10 @@ - - + + - + diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index aeeb3d614..12da1f57b 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + netstandard2.0 WorkflowCore.TestAssets WorkflowCore.TestAssets false diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 981d713c4..32f9c1363 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,16 +2,16 @@ netstandard2.0 - 2.3.1 - 2.3.1.0 - 2.3.1.0 + 2.3.0 + 2.3.0.0 + 2.3.0.0 Facilitates testing of workflows built on Workflow-Core - - - + + + diff --git a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index f275a05cf..c2563b8b2 100644 --- a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + netcoreapp2.2 false diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 0ed01e311..168be514f 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 false diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index cb4beac90..bec4d8561 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 WorkflowCore.Tests.MongoDB WorkflowCore.Tests.MongoDB true @@ -22,9 +22,9 @@ - - - + + + diff --git a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj index 66a792fe5..a255e01d6 100644 --- a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj +++ b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.1 + netcoreapp2.2 false diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 2fefbdea8..1cc7d56bd 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0 WorkflowCore.Tests.PostgreSQL WorkflowCore.Tests.PostgreSQL true @@ -21,7 +21,7 @@ - + diff --git a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj index 76857bc6d..c5ee9a6f8 100644 --- a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj +++ b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.1 + netcoreapp2.2 false diff --git a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj index 497293aaf..011714983 100644 --- a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj +++ b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 diff --git a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj index 9598398f4..9ce1b77cd 100644 --- a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj +++ b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index bc8321631..294adbd23 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 WorkflowCore.UnitTests WorkflowCore.UnitTests true @@ -20,9 +20,9 @@ - - - + + + From e73e1a46134b6f0fb216f2d3872e6c0c1ecabfdf Mon Sep 17 00:00:00 2001 From: glucaci Date: Thu, 3 Dec 2020 10:45:38 +0100 Subject: [PATCH 250/462] Add options to control which background task is starting --- src/WorkflowCore/Models/WorkflowOptions.cs | 5 ++++ .../ServiceCollectionExtensions.cs | 24 +++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index 44f1f1375..f6e370283 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -34,6 +34,11 @@ public WorkflowOptions(IServiceCollection services) EventHubFactory = new Func(sp => new SingleNodeEventHub(sp.GetService())); } + public bool DisableWorkflowConsumer { get; set; } = false; + public bool DisableEventConsumer { get; set; } = false; + public bool DisableIndexConsumer { get; set; } = false; + public bool DisableRunnablePoller { get; set; } = false; + public void UsePersistence(Func factory) { PersistanceFactory = factory; diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 8598abe03..d56b4484b 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -37,10 +37,26 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(options); services.AddSingleton(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + if (!options.DisableWorkflowConsumer) + { + services.AddTransient(); + } + + if (!options.DisableEventConsumer) + { + services.AddTransient(); + } + + if (!options.DisableIndexConsumer) + { + services.AddTransient(); + } + + if (!options.DisableRunnablePoller) + { + services.AddTransient(); + } + services.AddTransient(sp => sp.GetService()); services.AddTransient(); From 910974be6de8e1a046464c62eb9c5a170424db88 Mon Sep 17 00:00:00 2001 From: DanilF Date: Tue, 1 Dec 2020 16:43:30 -0500 Subject: [PATCH 251/462] Move WorkflowMiddlewareRunner and StepExecutor to be instantiated in a scope specific to the step to avoid potential threading issues when used with EFCore and DbContext --- src/WorkflowCore/ServiceCollectionExtensions.cs | 6 +++--- src/WorkflowCore/Services/WorkflowController.cs | 10 ++++++---- src/WorkflowCore/Services/WorkflowExecutor.cs | 15 ++++++++------- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- .../Services/WorkflowExecutorFixture.cs | 10 +++++++++- 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 8598abe03..bc15d0dd5 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -51,10 +51,10 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 20b7621d9..2a485c632 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -21,10 +21,9 @@ public class WorkflowController : IWorkflowController private readonly IExecutionPointerFactory _pointerFactory; private readonly ILifeCycleEventHub _eventHub; private readonly IServiceProvider _serviceProvider; - private readonly IWorkflowMiddlewareRunner _middlewareRunner; private readonly ILogger _logger; - public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowMiddlewareRunner middlewareRunner) + public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory, IServiceProvider serviceProvider) { _persistenceStore = persistenceStore; _lockProvider = lockProvider; @@ -33,7 +32,6 @@ public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLoc _pointerFactory = pointerFactory; _eventHub = eventHub; _serviceProvider = serviceProvider; - _middlewareRunner = middlewareRunner; _logger = loggerFactory.CreateLogger(); } @@ -85,7 +83,11 @@ public async Task StartWorkflow(string workflowId, int? version, wf.ExecutionPointers.Add(_pointerFactory.BuildGenesisPointer(def)); - await _middlewareRunner.RunPreMiddleware(wf, def); + using (var scope = _serviceProvider.CreateScope()) + { + var middlewareRunner = scope.ServiceProvider.GetRequiredService(); + await middlewareRunner.RunPreMiddleware(wf, def); + } string id = await _persistenceStore.CreateNewWorkflow(wf); await _queueProvider.QueueWork(id, QueueType.Workflow); diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index b0987c27d..c6241fd64 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -22,12 +22,10 @@ public class WorkflowExecutor : IWorkflowExecutor private readonly ICancellationProcessor _cancellationProcessor; private readonly ILifeCycleEventPublisher _publisher; private readonly WorkflowOptions _options; - private readonly IStepExecutor _stepExecutor; - private readonly IWorkflowMiddlewareRunner _middlewareRunner; private IWorkflowHost Host => _serviceProvider.GetService(); - public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IScopeProvider scopeProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, ICancellationProcessor cancellationProcessor, WorkflowOptions options, IWorkflowMiddlewareRunner middlewareRunner, IStepExecutor stepExecutor, ILoggerFactory loggerFactory) + public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IScopeProvider scopeProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, ICancellationProcessor cancellationProcessor, WorkflowOptions options, ILoggerFactory loggerFactory) { _serviceProvider = serviceProvider; _scopeProvider = scopeProvider; @@ -38,8 +36,6 @@ public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProv _options = options; _logger = loggerFactory.CreateLogger(); _executionResultProcessor = executionResultProcessor; - _middlewareRunner = middlewareRunner; - _stepExecutor = stepExecutor; } public async Task Execute(WorkflowInstance workflow, CancellationToken cancellationToken = default) @@ -157,6 +153,7 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); IStepBody body = step.ConstructBody(scope.ServiceProvider); + var stepExecutor = scope.ServiceProvider.GetRequiredService(); if (body == null) { @@ -185,7 +182,7 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe return; } - var result = await _stepExecutor.ExecuteStep(context, body); + var result = await stepExecutor.ExecuteStep(context, body); if (result.Proceed) { @@ -250,7 +247,11 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.UtcNow; - await _middlewareRunner.RunPostMiddleware(workflow, def); + using (var scope = _serviceProvider.CreateScope()) + { + var middlewareRunner = scope.ServiceProvider.GetRequiredService(); + await middlewareRunner.RunPostMiddleware(workflow, def); + } _publisher.PublishNotification(new WorkflowCompleted() { diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 53cd19bcd..36e9f45c1 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.2 - 3.3.2.0 - 3.3.2.0 + 3.3.3 + 3.3.3.0 + 3.3.3.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.2 + 3.3.3 diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 8b6f22e22..cefaf9a23 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -51,6 +51,14 @@ public WorkflowExecutorFixture() A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); A.CallTo(() => DateTimeProvider.UtcNow).Returns(DateTime.UtcNow); + A + .CallTo(() => ServiceProvider.GetService(typeof(IWorkflowMiddlewareRunner))) + .Returns(MiddlewareRunner); + + A + .CallTo(() => ServiceProvider.GetService(typeof(IStepExecutor))) + .Returns(StepExecutor); + A.CallTo(() => MiddlewareRunner .RunPostMiddleware(A._, A._)) .Returns(Task.CompletedTask); @@ -64,7 +72,7 @@ public WorkflowExecutorFixture() var loggerFactory = new LoggerFactory(); //loggerFactory.AddConsole(LogLevel.Debug); - Subject = new WorkflowExecutor(Registry, ServiceProvider, ScopeProvider, DateTimeProvider, ResultProcesser, EventHub, CancellationProcessor, Options, MiddlewareRunner, StepExecutor, loggerFactory); + Subject = new WorkflowExecutor(Registry, ServiceProvider, ScopeProvider, DateTimeProvider, ResultProcesser, EventHub, CancellationProcessor, Options, loggerFactory); } [Fact(DisplayName = "Should execute active step")] From 3e72d297e0b29cd2cddcc8450b911da4918d2914 Mon Sep 17 00:00:00 2001 From: Dzianis Dziarkach Date: Thu, 10 Dec 2020 11:44:48 +0300 Subject: [PATCH 252/462] added DateTimeProvider to usages #issue-721 --- src/WorkflowCore/Services/ActivityController.cs | 4 ++-- .../Services/BackgroundTasks/RunnablePoller.cs | 8 +++++--- .../Services/CancellationProcessor.cs | 8 +++++--- src/WorkflowCore/Services/GreyList.cs | 8 +++++--- src/WorkflowCore/Services/SyncWorkflowRunner.cs | 6 ++++-- src/WorkflowCore/Services/WorkflowController.cs | 16 +++++++++------- .../ServiceCollectionExtensions.cs | 4 ++-- .../Services/DynamoLockProvider.cs | 10 ++++++---- .../Services/KinesisStreamConsumer.cs | 8 +++++--- .../Services/RedisPersistenceProvider.cs | 2 +- 10 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/WorkflowCore/Services/ActivityController.cs b/src/WorkflowCore/Services/ActivityController.cs index e8d1d727b..f39bb64b2 100644 --- a/src/WorkflowCore/Services/ActivityController.cs +++ b/src/WorkflowCore/Services/ActivityController.cs @@ -28,10 +28,10 @@ public ActivityController(ISubscriptionRepository subscriptionRepository, IWorkf public async Task GetPendingActivity(string activityName, string workerId, TimeSpan? timeout = null) { - var endTime = DateTime.UtcNow.Add(timeout ?? TimeSpan.Zero); + var endTime = _dateTimeProvider.UtcNow.Add(timeout ?? TimeSpan.Zero); var firstPass = true; EventSubscription subscription = null; - while ((subscription == null && DateTime.UtcNow < endTime) || firstPass) + while ((subscription == null && _dateTimeProvider.UtcNow < endTime) || firstPass) { if (!firstPass) await Task.Delay(100); diff --git a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs index 0800fe582..6a6bdf79c 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs @@ -15,15 +15,17 @@ internal class RunnablePoller : IBackgroundTask private readonly ILogger _logger; private readonly IGreyList _greylist; private readonly WorkflowOptions _options; + private readonly IDateTimeProvider _dateTimeProvider; private Timer _pollTimer; - public RunnablePoller(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IGreyList greylist, WorkflowOptions options) + public RunnablePoller(IPersistenceProvider persistenceStore, IQueueProvider queueProvider, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IWorkflowRegistry registry, IDistributedLockProvider lockProvider, IGreyList greylist, IDateTimeProvider dateTimeProvider, WorkflowOptions options) { _persistenceStore = persistenceStore; _greylist = greylist; _queueProvider = queueProvider; _logger = loggerFactory.CreateLogger(); _lockProvider = lockProvider; + _dateTimeProvider = dateTimeProvider; _options = options; } @@ -54,7 +56,7 @@ private async void PollRunnables(object target) try { _logger.LogInformation("Polling for runnable workflows"); - var runnables = await _persistenceStore.GetRunnableInstances(DateTime.Now); + var runnables = await _persistenceStore.GetRunnableInstances(_dateTimeProvider.Now); foreach (var item in runnables) { if (_greylist.Contains($"wf:{item}")) @@ -85,7 +87,7 @@ private async void PollRunnables(object target) try { _logger.LogInformation("Polling for unprocessed events"); - var events = await _persistenceStore.GetRunnableEvents(DateTime.Now); + var events = await _persistenceStore.GetRunnableEvents(_dateTimeProvider.Now); foreach (var item in events.ToList()) { if (_greylist.Contains($"evt:{item}")) diff --git a/src/WorkflowCore/Services/CancellationProcessor.cs b/src/WorkflowCore/Services/CancellationProcessor.cs index 74c7a8a60..3ee58e287 100644 --- a/src/WorkflowCore/Services/CancellationProcessor.cs +++ b/src/WorkflowCore/Services/CancellationProcessor.cs @@ -12,11 +12,13 @@ public class CancellationProcessor : ICancellationProcessor { protected readonly ILogger _logger; private readonly IExecutionResultProcessor _executionResultProcessor; + private readonly IDateTimeProvider _dateTimeProvider; - public CancellationProcessor(IExecutionResultProcessor executionResultProcessor, ILoggerFactory logFactory) + public CancellationProcessor(IExecutionResultProcessor executionResultProcessor, ILoggerFactory logFactory, IDateTimeProvider dateTimeProvider) { _executionResultProcessor = executionResultProcessor; _logger = logFactory.CreateLogger(); + _dateTimeProvider = dateTimeProvider; } public void ProcessCancellations(WorkflowInstance workflow, WorkflowDefinition workflowDef, WorkflowExecutorResult executionResult) @@ -44,13 +46,13 @@ public void ProcessCancellations(WorkflowInstance workflow, WorkflowDefinition w _executionResultProcessor.ProcessExecutionResult(workflow, workflowDef, ptr, step, ExecutionResult.Next(), executionResult); } - ptr.EndTime = DateTime.Now.ToUniversalTime(); + ptr.EndTime = _dateTimeProvider.UtcNow; ptr.Active = false; ptr.Status = PointerStatus.Cancelled; foreach (var descendent in workflow.ExecutionPointers.FindByScope(ptr.Id).Where(x => x.Status != PointerStatus.Complete && x.Status != PointerStatus.Cancelled)) { - descendent.EndTime = DateTime.Now.ToUniversalTime(); + descendent.EndTime = _dateTimeProvider.UtcNow; descendent.Active = false; descendent.Status = PointerStatus.Cancelled; } diff --git a/src/WorkflowCore/Services/GreyList.cs b/src/WorkflowCore/Services/GreyList.cs index ec1148291..85b67fbc8 100644 --- a/src/WorkflowCore/Services/GreyList.cs +++ b/src/WorkflowCore/Services/GreyList.cs @@ -11,19 +11,21 @@ public class GreyList : IGreyList, IDisposable private readonly Timer _cycleTimer; private readonly ConcurrentDictionary _list; private readonly ILogger _logger; + private readonly IDateTimeProvider _dateTimeProvider; private const int CYCLE_TIME = 30; private const int TTL = 5; - public GreyList(ILoggerFactory loggerFactory) + public GreyList(ILoggerFactory loggerFactory, IDateTimeProvider dateTimeProvider) { _logger = loggerFactory.CreateLogger(); + _dateTimeProvider = dateTimeProvider; _list = new ConcurrentDictionary(); _cycleTimer = new Timer(new TimerCallback(Cycle), null, TimeSpan.FromMinutes(CYCLE_TIME), TimeSpan.FromMinutes(CYCLE_TIME)); } public void Add(string id) { - _list.AddOrUpdate(id, DateTime.Now, (key, val) => DateTime.Now); + _list.AddOrUpdate(id, _dateTimeProvider.Now, (key, val) => _dateTimeProvider.Now); } public bool Contains(string id) @@ -31,7 +33,7 @@ public bool Contains(string id) if (!_list.TryGetValue(id, out var start)) return false; - var result = start > (DateTime.Now.AddMinutes(-1 * TTL)); + var result = start > (_dateTimeProvider.Now.AddMinutes(-1 * TTL)); if (!result) _list.TryRemove(id, out var _); diff --git a/src/WorkflowCore/Services/SyncWorkflowRunner.cs b/src/WorkflowCore/Services/SyncWorkflowRunner.cs index ab1b84469..65bf6c626 100644 --- a/src/WorkflowCore/Services/SyncWorkflowRunner.cs +++ b/src/WorkflowCore/Services/SyncWorkflowRunner.cs @@ -18,8 +18,9 @@ public class SyncWorkflowRunner : ISyncWorkflowRunner private readonly IPersistenceProvider _persistenceStore; private readonly IExecutionPointerFactory _pointerFactory; private readonly IQueueProvider _queueService; + private readonly IDateTimeProvider _dateTimeProvider; - public SyncWorkflowRunner(IWorkflowHost host, IWorkflowExecutor executor, IDistributedLockProvider lockService, IWorkflowRegistry registry, IPersistenceProvider persistenceStore, IExecutionPointerFactory pointerFactory, IQueueProvider queueService) + public SyncWorkflowRunner(IWorkflowHost host, IWorkflowExecutor executor, IDistributedLockProvider lockService, IWorkflowRegistry registry, IPersistenceProvider persistenceStore, IExecutionPointerFactory pointerFactory, IQueueProvider queueService, IDateTimeProvider dateTimeProvider) { _host = host; _executor = executor; @@ -28,6 +29,7 @@ public SyncWorkflowRunner(IWorkflowHost host, IWorkflowExecutor executor, IDistr _persistenceStore = persistenceStore; _pointerFactory = pointerFactory; _queueService = queueService; + _dateTimeProvider = dateTimeProvider; } public async Task RunWorkflowSync(string workflowId, int version, TData data, string reference, TimeSpan timeOut, bool persistSate = true) @@ -46,7 +48,7 @@ public async Task RunWorkflowSync(string workflowId, in Data = data, Description = def.Description, NextExecution = 0, - CreateTime = DateTime.Now.ToUniversalTime(), + CreateTime = _dateTimeProvider.UtcNow, Status = WorkflowStatus.Suspended, Reference = reference }; diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 2a485c632..690a6a596 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -22,8 +22,9 @@ public class WorkflowController : IWorkflowController private readonly ILifeCycleEventHub _eventHub; private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; + private readonly IDateTimeProvider _dateTimeProvider; - public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory, IServiceProvider serviceProvider) + public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLockProvider lockProvider, IWorkflowRegistry registry, IQueueProvider queueProvider, IExecutionPointerFactory pointerFactory, ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IDateTimeProvider dateTimeProvider) { _persistenceStore = persistenceStore; _lockProvider = lockProvider; @@ -33,6 +34,7 @@ public WorkflowController(IPersistenceProvider persistenceStore, IDistributedLoc _eventHub = eventHub; _serviceProvider = serviceProvider; _logger = loggerFactory.CreateLogger(); + _dateTimeProvider = dateTimeProvider; } public Task StartWorkflow(string workflowId, object data = null, string reference=null) @@ -68,7 +70,7 @@ public async Task StartWorkflow(string workflowId, int? version, Data = data, Description = def.Description, NextExecution = 0, - CreateTime = DateTime.Now.ToUniversalTime(), + CreateTime = _dateTimeProvider.UtcNow, Status = WorkflowStatus.Runnable, Reference = reference }; @@ -94,7 +96,7 @@ public async Task StartWorkflow(string workflowId, int? version, await _queueProvider.QueueWork(id, QueueType.Index); await _eventHub.PublishNotification(new WorkflowStarted() { - EventTimeUtc = DateTime.UtcNow, + EventTimeUtc = _dateTimeProvider.UtcNow, Reference = reference, WorkflowInstanceId = id, WorkflowDefinitionId = def.Id, @@ -111,7 +113,7 @@ public async Task PublishEvent(string eventName, string eventKey, object eventDa if (effectiveDate.HasValue) evt.EventTime = effectiveDate.Value.ToUniversalTime(); else - evt.EventTime = DateTime.Now.ToUniversalTime(); + evt.EventTime = _dateTimeProvider.UtcNow; evt.EventData = eventData; evt.EventKey = eventKey; @@ -137,7 +139,7 @@ public async Task SuspendWorkflow(string workflowId) await _queueProvider.QueueWork(workflowId, QueueType.Index); await _eventHub.PublishNotification(new WorkflowSuspended() { - EventTimeUtc = DateTime.UtcNow, + EventTimeUtc = _dateTimeProvider.UtcNow, Reference = wf.Reference, WorkflowInstanceId = wf.Id, WorkflowDefinitionId = wf.WorkflowDefinitionId, @@ -173,7 +175,7 @@ public async Task ResumeWorkflow(string workflowId) await _queueProvider.QueueWork(workflowId, QueueType.Index); await _eventHub.PublishNotification(new WorkflowResumed() { - EventTimeUtc = DateTime.UtcNow, + EventTimeUtc = _dateTimeProvider.UtcNow, Reference = wf.Reference, WorkflowInstanceId = wf.Id, WorkflowDefinitionId = wf.WorkflowDefinitionId, @@ -207,7 +209,7 @@ public async Task TerminateWorkflow(string workflowId) await _queueProvider.QueueWork(workflowId, QueueType.Index); await _eventHub.PublishNotification(new WorkflowTerminated() { - EventTimeUtc = DateTime.UtcNow, + EventTimeUtc = _dateTimeProvider.UtcNow, Reference = wf.Reference, WorkflowInstanceId = wf.Id, WorkflowDefinitionId = wf.WorkflowDefinitionId, diff --git a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs index bb43a3397..57b6f6bc8 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs @@ -21,7 +21,7 @@ public static WorkflowOptions UseAwsSimpleQueueService(this WorkflowOptions opti public static WorkflowOptions UseAwsDynamoLocking(this WorkflowOptions options, AWSCredentials credentials, AmazonDynamoDBConfig config, string tableName) { - options.UseDistributedLockManager(sp => new DynamoLockProvider(credentials, config, tableName, sp.GetService())); + options.UseDistributedLockManager(sp => new DynamoLockProvider(credentials, config, tableName, sp.GetService(), sp.GetService())); return options; } @@ -35,7 +35,7 @@ public static WorkflowOptions UseAwsDynamoPersistence(this WorkflowOptions optio public static WorkflowOptions UseAwsKinesis(this WorkflowOptions options, AWSCredentials credentials, RegionEndpoint region, string appName, string streamName) { options.Services.AddTransient(sp => new KinesisTracker(credentials, region, "workflowcore_kinesis", sp.GetService())); - options.Services.AddTransient(sp => new KinesisStreamConsumer(credentials, region, sp.GetService(), sp.GetService(), sp.GetService())); + options.Services.AddTransient(sp => new KinesisStreamConsumer(credentials, region, sp.GetService(), sp.GetService(), sp.GetService(), sp.GetService())); options.UseEventHub(sp => new KinesisProvider(credentials, region, appName, streamName, sp.GetService(), sp.GetService())); return options; } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs index 2a7c2f7bd..b10dc4365 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -23,14 +23,16 @@ public class DynamoLockProvider : IDistributedLockProvider private Task _heartbeatTask; private CancellationTokenSource _cancellationTokenSource; private readonly AutoResetEvent _mutex = new AutoResetEvent(true); + private readonly IDateTimeProvider _dateTimeProvider; - public DynamoLockProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, string tableName, ILoggerFactory logFactory) + public DynamoLockProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, string tableName, ILoggerFactory logFactory, IDateTimeProvider dateTimeProvider) { _logger = logFactory.CreateLogger(); _client = new AmazonDynamoDBClient(credentials, config); _localLocks = new List(); _tableName = tableName; _nodeId = Guid.NewGuid().ToString(); + _dateTimeProvider = dateTimeProvider; } public async Task AcquireLock(string Id, CancellationToken cancellationToken) @@ -46,7 +48,7 @@ public async Task AcquireLock(string Id, CancellationToken cancellationTok { "lock_owner", new AttributeValue(_nodeId) }, { "expires", new AttributeValue() { - N = Convert.ToString(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + _ttl) + N = Convert.ToString(new DateTimeOffset(_dateTimeProvider.UtcNow).ToUnixTimeMilliseconds() + _ttl) } } }, @@ -55,7 +57,7 @@ public async Task AcquireLock(string Id, CancellationToken cancellationTok { { ":expired", new AttributeValue() { - N = Convert.ToString(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + _jitter) + N = Convert.ToString(new DateTimeOffset(_dateTimeProvider.UtcNow).ToUnixTimeMilliseconds() + _jitter) } } } @@ -154,7 +156,7 @@ private async void SendHeartbeat() { "lock_owner", new AttributeValue(_nodeId) }, { "expires", new AttributeValue() { - N = Convert.ToString(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + _ttl) + N = Convert.ToString(new DateTimeOffset(_dateTimeProvider.UtcNow).ToUnixTimeMilliseconds() + _ttl) } } }, diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs index c22e40fce..2a3a42494 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs @@ -23,8 +23,9 @@ public class KinesisStreamConsumer : IKinesisStreamConsumer, IDisposable private readonly Task _processTask; private readonly int _batchSize = 100; private ICollection _subscribers = new HashSet(); + private readonly IDateTimeProvider _dateTimeProvider; - public KinesisStreamConsumer(AWSCredentials credentials, RegionEndpoint region, IKinesisTracker tracker, IDistributedLockProvider lockManager, ILoggerFactory logFactory) + public KinesisStreamConsumer(AWSCredentials credentials, RegionEndpoint region, IKinesisTracker tracker, IDistributedLockProvider lockManager, ILoggerFactory logFactory, IDateTimeProvider dateTimeProvider) { _logger = logFactory.CreateLogger(GetType()); _tracker = tracker; @@ -32,6 +33,7 @@ public KinesisStreamConsumer(AWSCredentials credentials, RegionEndpoint region, _client = new AmazonKinesisClient(credentials, region); _processTask = new Task(Process); _processTask.Start(); + _dateTimeProvider = dateTimeProvider; } public async Task Subscribe(string appName, string stream, Action action) @@ -59,7 +61,7 @@ private async void Process() { try { - var todo = _subscribers.Where(x => x.Snooze < DateTime.Now).ToList(); + var todo = _subscribers.Where(x => x.Snooze < _dateTimeProvider.Now).ToList(); foreach (var sub in todo) { if (!await _lockManager.AcquireLock($"{sub.AppName}.{sub.Stream}.{sub.Shard.ShardId}", @@ -71,7 +73,7 @@ private async void Process() var records = await GetBatch(sub); if (records.Records.Count == 0) - sub.Snooze = DateTime.Now.AddSeconds(5); + sub.Snooze = _dateTimeProvider.Now.AddSeconds(5); var lastSequence = string.Empty; diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index 79a15b970..103e7c442 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -62,7 +62,7 @@ public async Task PersistWorkflow(WorkflowInstance workflow) public async Task> GetRunnableInstances(DateTime asAt) { var result = new List(); - var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{WORKFLOW_SET}.{RUNNABLE_INDEX}", -1, DateTime.UtcNow.Ticks); + var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{WORKFLOW_SET}.{RUNNABLE_INDEX}", -1, asAt.ToUniversalTime().Ticks); foreach (var item in data) result.Add(item); From e609f7f0aba88068fa81ced7cf7924ee0a4e7f50 Mon Sep 17 00:00:00 2001 From: glucaci Date: Sun, 13 Dec 2020 13:32:12 +0100 Subject: [PATCH 253/462] Naming --- src/WorkflowCore/Models/WorkflowOptions.cs | 8 ++++---- src/WorkflowCore/ServiceCollectionExtensions.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index f6e370283..bf2d28e06 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -34,10 +34,10 @@ public WorkflowOptions(IServiceCollection services) EventHubFactory = new Func(sp => new SingleNodeEventHub(sp.GetService())); } - public bool DisableWorkflowConsumer { get; set; } = false; - public bool DisableEventConsumer { get; set; } = false; - public bool DisableIndexConsumer { get; set; } = false; - public bool DisableRunnablePoller { get; set; } = false; + public bool EnableWorkflows { get; set; } = true; + public bool EnableEvents { get; set; } = true; + public bool EnableIndexes { get; set; } = true; + public bool EnablePolling { get; set; } = true; public void UsePersistence(Func factory) { diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index d56b4484b..feb10c726 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -37,22 +37,22 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(options); services.AddSingleton(); - if (!options.DisableWorkflowConsumer) + if (!options.EnableWorkflows) { services.AddTransient(); } - if (!options.DisableEventConsumer) + if (!options.EnableEvents) { services.AddTransient(); } - if (!options.DisableIndexConsumer) + if (!options.EnableIndexes) { services.AddTransient(); } - if (!options.DisableRunnablePoller) + if (!options.EnablePolling) { services.AddTransient(); } From 8bcd41c7608a1b7134c0a37bf49143842b9a5907 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 14 Dec 2020 17:07:59 +0100 Subject: [PATCH 254/462] Reverse registration logic --- src/WorkflowCore/ServiceCollectionExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index feb10c726..0e4f17f8f 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -37,22 +37,22 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(options); services.AddSingleton(); - if (!options.EnableWorkflows) + if (options.EnableWorkflows) { services.AddTransient(); } - if (!options.EnableEvents) + if (options.EnableEvents) { services.AddTransient(); } - if (!options.EnableIndexes) + if (options.EnableIndexes) { services.AddTransient(); } - if (!options.EnablePolling) + if (options.EnablePolling) { services.AddTransient(); } From 19f90efafa6da0a382901d2da790c7c09413835f Mon Sep 17 00:00:00 2001 From: Stephen Brown Date: Tue, 15 Dec 2020 13:28:08 +0100 Subject: [PATCH 255/462] Upgrade to .net 5.0 and EF Core 5.0 --- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- ...rkflowCore.Persistence.EntityFramework.csproj | 14 +++++++------- .../WorkflowCore.Persistence.MySQL.csproj | 12 ++++++------ .../WorkflowCore.Persistence.PostgreSQL.csproj | 16 ++++++++-------- .../SqlServerContext.cs | 12 ++++++------ .../WorkflowCore.Persistence.SqlServer.csproj | 16 ++++++++-------- .../WorkflowCore.Persistence.Sqlite.csproj | 12 ++++++------ .../WorkflowCore.QueueProviders.SqlServer.csproj | 4 ++-- .../WorkflowCore.Sample01.csproj | 14 +++++++------- .../WorkflowCore.Sample02.csproj | 6 +++--- .../WorkflowCore.Sample03.csproj | 8 ++++---- .../WorkflowCore.Sample04.csproj | 8 ++++---- .../WorkflowCore.Sample05.csproj | 2 +- .../WorkflowCore.Sample06.csproj | 2 +- .../WorkflowCore.Sample07.csproj | 4 ++-- .../WorkflowCore.Sample08.csproj | 4 ++-- .../WorkflowCore.Sample09.csproj | 4 ++-- .../WorkflowCore.Sample09s.csproj | 6 +++--- .../WorkflowCore.Sample10.csproj | 4 ++-- .../WorkflowCore.Sample11.csproj | 6 +++--- .../WorkflowCore.Sample12.csproj | 4 ++-- .../WorkflowCore.Sample13.csproj | 4 ++-- .../WorkflowCore.Sample14.csproj | 2 +- .../WorkflowCore.Sample15.csproj | 6 +++--- .../WorkflowCore.Sample16.csproj | 6 +++--- .../WorkflowCore.Sample17.csproj | 4 ++-- .../WorkflowCore.Sample18.csproj | 12 ++++++------ .../WorkflowCore.Sample19.csproj | 12 ++++++------ .../WorkflowCore.TestSample01.csproj | 4 ++-- test/ScratchPad/ScratchPad.csproj | 6 +++--- .../WorkflowCore.IntegrationTests.csproj | 8 ++++---- .../WorkflowCore.TestAssets.csproj | 2 +- .../WorkflowCore.Testing.csproj | 12 ++++++------ .../WorkflowCore.Tests.DynamoDB.csproj | 2 +- .../WorkflowCore.Tests.Elasticsearch.csproj | 2 +- .../WorkflowCore.Tests.MongoDB.csproj | 8 ++++---- .../WorkflowCore.Tests.MySQL.csproj | 4 ++-- .../WorkflowCore.Tests.PostgreSQL.csproj | 4 ++-- .../WorkflowCore.Tests.Redis.csproj | 4 ++-- .../WorkflowCore.Tests.SqlServer.csproj | 2 +- .../WorkflowCore.Tests.Sqlite.csproj | 2 +- .../WorkflowCore.UnitTests.csproj | 8 ++++---- 42 files changed, 140 insertions(+), 140 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 53cd19bcd..36e9f45c1 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.2 - 3.3.2.0 - 3.3.2.0 + 3.3.3 + 3.3.3.0 + 3.3.3.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.2 + 3.3.3 diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index ed8392a62..214787365 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -3,7 +3,7 @@ Workflow Core EntityFramework Core Persistence Provider Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.EntityFramework WorkflowCore.Persistence.EntityFramework workflow;.NET;Core;state machine;WorkflowCore;EntityFramework;EntityFrameworkCore @@ -14,11 +14,11 @@ false false false - 3.1.0 + 3.3.3 Base package for Workflow-core peristence providers using entity framework - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.3.3.0 + 3.3.3.0 + 3.3.3 @@ -26,8 +26,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index a031e5fba..f1b09d045 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -4,7 +4,7 @@ Workflow Core MySQL Persistence Provider 1.0.0 Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.MySQL WorkflowCore.Persistence.MySQL workflow;.NET;Core;state machine;WorkflowCore;MySQL @@ -16,17 +16,17 @@ false false Provides support to persist workflows running on Workflow Core to a MySQL database. - 3.0.1 - 3.0.1.0 - 3.0.1.0 + 3.3.3 + 3.3.3.0 + 3.3.3.0 - + all runtime; build; native; contentfiles; analyzers - + diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 7addc52a4..a041d19b1 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,10 +15,10 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 3.1.0 - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.3.3 + 3.3.3.0 + 3.3.3.0 + 3.3.3 @@ -27,12 +27,12 @@ - - - + + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs index 499c10295..7383cce3e 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs @@ -28,37 +28,37 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void ConfigureSubscriptionStorage(EntityTypeBuilder builder) { builder.ToTable("Subscription", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureWorkflowStorage(EntityTypeBuilder builder) { builder.ToTable("Workflow", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureExecutionPointerStorage(EntityTypeBuilder builder) { builder.ToTable("ExecutionPointer", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureExecutionErrorStorage(EntityTypeBuilder builder) { builder.ToTable("ExecutionError", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureExetensionAttributeStorage(EntityTypeBuilder builder) { builder.ToTable("ExtensionAttribute", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureEventStorage(EntityTypeBuilder builder) { builder.ToTable("Event", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index be52279d1..427bbbdf3 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -4,7 +4,7 @@ Workflow Core SQL Server Persistence Provider 1.8.0 Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.SqlServer WorkflowCore.Persistence.SqlServer workflow;.NET;Core;state machine;WorkflowCore @@ -15,11 +15,11 @@ false false false - 3.1.0 + 3.3.3 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.3.3.0 + 3.3.3.0 + 3.3.3 @@ -28,11 +28,11 @@ - - + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 2d2789021..ddcc8deac 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -4,7 +4,7 @@ Workflow Core Sqlite Persistence Provider 1.5.0 Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.Sqlite WorkflowCore.Persistence.Sqlite workflow;.NET;Core;state machine;WorkflowCore;Sqlite @@ -16,10 +16,10 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 3.1.0 - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.3.3 + 3.3.3.0 + 3.3.3.0 + 3.3.3 @@ -28,7 +28,7 @@ - + diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 36d7cda77..97b48d220 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -5,7 +5,7 @@ Roberto Paterlini Queue provider for Workflow-core using SQL Server Service Broker - 1.0.3-alpha + 1.0.4-alpha @@ -20,7 +20,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj index e4a09e491..5f4857cca 100644 --- a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj +++ b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.Sample01 Exe WorkflowCore.Sample01 @@ -16,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj index 0765d50c3..7fab128b6 100644 --- a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj +++ b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.Sample02 Exe WorkflowCore.Sample02 @@ -11,8 +11,8 @@ - - + + diff --git a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj index fc4a5d4fd..7b332dcd9 100644 --- a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj +++ b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp3.1 WorkflowCore.Sample03 Exe WorkflowCore.Sample03 @@ -16,9 +16,9 @@ - - - + + + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index 82107b0e6..af4dd01f9 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 WorkflowCore.Sample04 Exe WorkflowCore.Sample04 @@ -29,9 +29,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj index 592fa228b..069931ef0 100644 --- a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj +++ b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj index b61478b9e..d4c71d9aa 100644 --- a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj +++ b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index 5ca51a866..2f5937f36 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 true WorkflowCore.Sample07 Exe @@ -26,7 +26,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj index 3ff429e28..2a556e45d 100644 --- a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj +++ b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 WorkflowCore.Sample08 Exe WorkflowCore.Sample08 @@ -21,7 +21,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj index 6f75a0d7d..bc16eb974 100644 --- a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj +++ b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj index e569ec895..bc16eb974 100644 --- a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj +++ b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj @@ -1,12 +1,12 @@ - + Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj index 6f75a0d7d..bc16eb974 100644 --- a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj +++ b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj index b6454f6a2..4eb65c992 100644 --- a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj +++ b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp2.2 + netcoreapp3.1 - - + + diff --git a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj index 9879baab2..5b030fb75 100644 --- a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj +++ b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj index dbe49166e..7f814f488 100644 --- a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj +++ b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp2.2 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj index fef8173cc..974472a25 100644 --- a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj +++ b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.1 diff --git a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj index c63fbd792..9fd7656d7 100644 --- a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj +++ b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp2.2 + netcoreapp3.1 - - + + diff --git a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj index c63fbd792..9fd7656d7 100644 --- a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj +++ b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp2.2 + netcoreapp3.1 - - + + diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index 26933096d..0c1689366 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj index 660b47511..46ad70bf5 100644 --- a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj +++ b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj @@ -1,15 +1,15 @@ - + Exe - netcoreapp3.0 + netcoreapp3.1 - - - - + + + + diff --git a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj index b2a7796c3..f1fbd9c5c 100644 --- a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj +++ b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj @@ -1,15 +1,15 @@ - + Exe - netcoreapp3.0 + netcoreapp3.1 - - - - + + + + diff --git a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj index 282fdbb15..789e6e445 100644 --- a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj +++ b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj @@ -1,12 +1,12 @@  - netcoreapp2.2 + netcoreapp3.1 - + diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 4fb61dd24..27c262350 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 ScratchPad Exe ScratchPad @@ -11,8 +11,8 @@ - - + + diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index 971f10c57..8739a6c6d 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.IntegrationTests WorkflowCore.IntegrationTests true @@ -21,10 +21,10 @@ - - + + - + diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index 12da1f57b..aeeb3d614 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.1 WorkflowCore.TestAssets WorkflowCore.TestAssets false diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 32f9c1363..981d713c4 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,16 +2,16 @@ netstandard2.0 - 2.3.0 - 2.3.0.0 - 2.3.0.0 + 2.3.1 + 2.3.1.0 + 2.3.1.0 Facilitates testing of workflows built on Workflow-Core - - - + + + diff --git a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index c2563b8b2..f275a05cf 100644 --- a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -1,7 +1,7 @@ - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 168be514f..0ed01e311 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index bec4d8561..cb4beac90 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.Tests.MongoDB WorkflowCore.Tests.MongoDB true @@ -22,9 +22,9 @@ - - - + + + diff --git a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj index a255e01d6..66a792fe5 100644 --- a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj +++ b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj @@ -1,7 +1,7 @@ - + - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 1cc7d56bd..2fefbdea8 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 WorkflowCore.Tests.PostgreSQL WorkflowCore.Tests.PostgreSQL true @@ -21,7 +21,7 @@ - + diff --git a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj index c5ee9a6f8..76857bc6d 100644 --- a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj +++ b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj @@ -1,7 +1,7 @@ - + - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj index 011714983..497293aaf 100644 --- a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj +++ b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 diff --git a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj index 9ce1b77cd..9598398f4 100644 --- a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj +++ b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index 294adbd23..bc8321631 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.UnitTests WorkflowCore.UnitTests true @@ -20,9 +20,9 @@ - - - + + + From 0e2d0fa03e119e977d1981f6da069a75a41bde9a Mon Sep 17 00:00:00 2001 From: Stephen Brown Date: Tue, 15 Dec 2020 13:34:28 +0100 Subject: [PATCH 256/462] Upgrade to EF Core 5.0.1 --- .../WorkflowCore.Persistence.EntityFramework.csproj | 4 ++-- .../WorkflowCore.Persistence.MySQL.csproj | 2 +- .../WorkflowCore.Persistence.PostgreSQL.csproj | 4 ++-- .../WorkflowCore.Persistence.SqlServer.csproj | 6 +++--- .../WorkflowCore.Persistence.Sqlite.csproj | 2 +- .../WorkflowCore.Sample01/WorkflowCore.Sample01.csproj | 2 +- .../WorkflowCore.Sample04/WorkflowCore.Sample04.csproj | 2 +- .../WorkflowCore.Sample11/WorkflowCore.Sample11.csproj | 2 +- .../WorkflowCore.Sample15/WorkflowCore.Sample15.csproj | 2 +- .../WorkflowCore.Sample16/WorkflowCore.Sample16.csproj | 2 +- .../WorkflowCore.Sample17/WorkflowCore.Sample17.csproj | 2 +- .../WorkflowCore.Sample18/WorkflowCore.Sample18.csproj | 2 +- .../WorkflowCore.Sample19/WorkflowCore.Sample19.csproj | 2 +- .../WorkflowCore.TestSample01.csproj | 2 +- test/ScratchPad/ScratchPad.csproj | 2 +- .../WorkflowCore.IntegrationTests.csproj | 2 +- test/WorkflowCore.Testing/WorkflowCore.Testing.csproj | 2 +- .../WorkflowCore.Tests.MongoDB.csproj | 2 +- test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj | 2 +- 19 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 214787365..a08f951d9 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -26,8 +26,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index f1b09d045..f40289070 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -22,7 +22,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index a041d19b1..a5faadf44 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -29,10 +29,10 @@ - + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 427bbbdf3..835f26433 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -28,11 +28,11 @@ - - + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index ddcc8deac..44e1e344e 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj index 5f4857cca..0e878eb0b 100644 --- a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj +++ b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index af4dd01f9..af988640d 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -25,7 +25,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj index 4eb65c992..5a8923eeb 100644 --- a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj +++ b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj index 9fd7656d7..79ed2ba57 100644 --- a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj +++ b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj index 9fd7656d7..79ed2ba57 100644 --- a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj +++ b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index 0c1689366..766fc8e0c 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj index 46ad70bf5..5f9b64824 100644 --- a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj +++ b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj index f1fbd9c5c..0f169bcc8 100644 --- a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj +++ b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj index 789e6e445..71454dc11 100644 --- a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj +++ b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj @@ -6,7 +6,7 @@ - + diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 27c262350..4da4684ed 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -11,7 +11,7 @@ - + diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index 8739a6c6d..f7634e8a9 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -21,7 +21,7 @@ - + diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 981d713c4..22b68fb2f 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index cb4beac90..1523f31ea 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -22,7 +22,7 @@ - + diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index bc8321631..5ec1e2ff1 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -20,7 +20,7 @@ - + From dee17c27e0b18ed22293756b38bb1502546b4fb9 Mon Sep 17 00:00:00 2001 From: "xavid.ramirez@apchomehealth.onmicrosoft.com" Date: Wed, 16 Dec 2020 14:12:32 -0600 Subject: [PATCH 257/462] Adding RavenDB persistence provider --- WorkflowCore.sln | 7 + .../RavenStoreOptions.cs | 14 + .../ServiceCollectionExtensions.cs | 39 +++ .../Services/RavenDbIndexes.cs | 14 + .../Services/RavendbPersistenceProvider.cs | 320 ++++++++++++++++++ .../Services/WorkflowPurger.cs | 42 +++ .../WorkflowCore.Persistence.RavenDB.csproj | 32 ++ 7 files changed, 468 insertions(+) create mode 100644 src/providers/WorkflowCore.Persistence.RavenDB/RavenStoreOptions.cs create mode 100644 src/providers/WorkflowCore.Persistence.RavenDB/ServiceCollectionExtensions.cs create mode 100644 src/providers/WorkflowCore.Persistence.RavenDB/Services/RavenDbIndexes.cs create mode 100644 src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs create mode 100644 src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs create mode 100644 src/providers/WorkflowCore.Persistence.RavenDB/WorkflowCore.Persistence.RavenDB.csproj diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 1ef2a187f..be18fd721 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -150,6 +150,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.QueuePro EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample19", "src\samples\WorkflowCore.Sample19\WorkflowCore.Sample19.csproj", "{1223ED47-3E5E-4960-B70D-DFAF550F6666}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -368,6 +370,10 @@ Global {1223ED47-3E5E-4960-B70D-DFAF550F6666}.Debug|Any CPU.Build.0 = Debug|Any CPU {1223ED47-3E5E-4960-B70D-DFAF550F6666}.Release|Any CPU.ActiveCfg = Release|Any CPU {1223ED47-3E5E-4960-B70D-DFAF550F6666}.Release|Any CPU.Build.0 = Release|Any CPU + {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -429,6 +435,7 @@ Global {51BB7DCD-01DD-453D-A1E7-17E5E3DBB14C} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {54DE20BA-EBA7-4BF0-9BD9-F03766849716} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {1223ED47-3E5E-4960-B70D-DFAF550F6666} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} + {AF205715-C8B7-42EF-BF14-AFC9E7F27242} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/RavenStoreOptions.cs b/src/providers/WorkflowCore.Persistence.RavenDB/RavenStoreOptions.cs new file mode 100644 index 000000000..00a420668 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.RavenDB/RavenStoreOptions.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Persistence.RavenDB +{ + public class RavenStoreOptions + { + public string ServerUrl { get; set; } + public string DatabaseName { get; set; } + public string CertificatePath { get; set; } + public string CertificatePassword { get; set; } + } +} diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.RavenDB/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..4b7d12926 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.RavenDB/ServiceCollectionExtensions.cs @@ -0,0 +1,39 @@ +using Raven.Client.Documents.Operations; +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Models; +using Raven.Client; +using Raven.Client.Documents; +using System.Security.Cryptography.X509Certificates; +using WorkflowCore.Persistence.RavenDB.Services; +using WorkflowCore.Interface; +using WorkflowCore.Persistence.RavenDB; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + public static WorkflowOptions UseRavenDB(this WorkflowOptions options, RavenStoreOptions configOptions) + { + IDocumentStore store = new DocumentStore + { + Urls = new[] { configOptions.ServerUrl }, + Database = configOptions.DatabaseName, + Certificate = new X509Certificate2(configOptions.CertificatePath, configOptions.CertificatePassword) + }.Initialize(); + + options.UsePersistence(sp => + { + return new RavendbPersistenceProvider(store); + }); + + options.Services.AddTransient(sp => + { + return new WorkflowPurger(store); + }); + + return options; + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavenDbIndexes.cs b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavenDbIndexes.cs new file mode 100644 index 000000000..75f5ab106 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavenDbIndexes.cs @@ -0,0 +1,14 @@ +using Raven.Client.Documents.Indexes; +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Models; + +namespace WorkflowCore.Persistence.RavenDB.Services +{ + // TODO: Implement Map for result bind of Index + public class WorkflowInstances_Id : AbstractIndexCreationTask { } + public class EventSubscriptions_Id : AbstractIndexCreationTask { } + public class Events_Id : AbstractIndexCreationTask { } + public class ExecutionErrors_Id : AbstractIndexCreationTask { } +} diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs new file mode 100644 index 000000000..66d38b5aa --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs @@ -0,0 +1,320 @@ +using Raven.Client.Documents; +using Raven.Client.Documents.Indexes; +using Raven.Client.Documents.Linq; +using Raven.Client.Documents.Operations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Persistence.RavenDB.Services +{ + public class RavendbPersistenceProvider : IPersistenceProvider + { + internal const string WorkflowCollectionName = "wfc.workflows"; + private readonly IDocumentStore _database; + static bool indexesCreated = false; + + public RavendbPersistenceProvider(IDocumentStore database) + { + _database = database; + CreateIndexes(this); + } + + static void CreateIndexes(RavendbPersistenceProvider instance) + { + if (!indexesCreated) + { + /* + // create the indexes here based on assemby of classes in the file 'RavenDbIndexes.cs' + IndexCreation.CreateIndexes(typeof(WorkflowInstances_Id).Assembly, instance._database); + IndexCreation.CreateIndexes(typeof(EventSubscriptions_Id).Assembly, instance._database); + IndexCreation.CreateIndexes(typeof(Events_Id).Assembly, instance._database); + IndexCreation.CreateIndexes(typeof(ExecutionErrors_Id).Assembly, instance._database); + */ + indexesCreated = true; + } + } + + public async Task CreateNewWorkflow(WorkflowInstance workflow) + { + using (var session = _database.OpenAsyncSession()) + { + await session.StoreAsync(workflow); + var id = workflow.Id; + await session.SaveChangesAsync(); + return id; + } + } + + public async Task PersistWorkflow(WorkflowInstance workflow) + { + using (var session = _database.OpenAsyncSession()) + { + session.Advanced.Patch(workflow.Id, x => x.WorkflowDefinitionId, workflow.WorkflowDefinitionId); + session.Advanced.Patch(workflow.Id, x => x.Version, workflow.Version); + session.Advanced.Patch(workflow.Id, x => x.Description, workflow.Description); + session.Advanced.Patch(workflow.Id, x => x.Reference, workflow.Reference); + session.Advanced.Patch(workflow.Id, x => x.ExecutionPointers, workflow.ExecutionPointers); + session.Advanced.Patch(workflow.Id, x => x.NextExecution, workflow.NextExecution); + session.Advanced.Patch(workflow.Id, x => x.Status, workflow.Status); + session.Advanced.Patch(workflow.Id, x => x.Data, workflow.Data); + session.Advanced.Patch(workflow.Id, x => x.CreateTime, workflow.CreateTime); + session.Advanced.Patch(workflow.Id, x => x.CompleteTime, workflow.CompleteTime); + + await session.SaveChangesAsync(); + } + } + + public async Task> GetRunnableInstances(DateTime asAt) + { + var now = asAt.ToUniversalTime().Ticks; + using (var session = _database.OpenAsyncSession()) + { + var l = session.Query().Where(w => w.NextExecution.HasValue + && (w.NextExecution <= now) + && (w.Status == WorkflowStatus.Runnable) + ).Select(x => x.Id); + + return await l.ToListAsync(); + } + } + + public async Task GetWorkflowInstance(string Id) + { + using (var session = _database.OpenAsyncSession()) + { + var result = await session.Query().FirstOrDefaultAsync(x => x.Id == Id); + return result; + } + } + + public async Task> GetWorkflowInstances(IEnumerable ids) + { + if (ids == null) + { + return new List(); + } + + using (var session = _database.OpenAsyncSession()) + { + var list = session.Query().Where(x => x.Id.In(ids)); + return await list.ToListAsync(); + } + } + + public async Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) + { + using (var session = _database.OpenAsyncSession()) + { + var result = session.Query(); + + if (status.HasValue) + result = result.Where(x => x.Status == status.Value); + + if (!String.IsNullOrEmpty(type)) + result = result.Where(x => x.WorkflowDefinitionId == type); + + if (createdFrom.HasValue) + result = result.Where(x => x.CreateTime >= createdFrom.Value); + + if (createdTo.HasValue) + result = result.Where(x => x.CreateTime <= createdTo.Value); + + return await result.Skip(skip).Take(take).ToListAsync(); + } + } + + public async Task CreateEventSubscription(EventSubscription subscription) + { + using (var session = _database.OpenAsyncSession()) + { + await session.StoreAsync(subscription); + var id = subscription.Id; + await session.SaveChangesAsync(); + return id; + } + } + + public async Task TerminateSubscription(string eventSubscriptionId) + { + using (var session = _database.OpenAsyncSession()) + { + session.Delete(eventSubscriptionId); + await Task.CompletedTask; + } + } + + public async Task GetSubscription(string eventSubscriptionId) + { + using (var session = _database.OpenAsyncSession()) + { + var result = session.Query().Where(x => x.Id == eventSubscriptionId); + return await result.FirstOrDefaultAsync(); + } + } + + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + { + using (var session = _database.OpenAsyncSession()) + { + var q = session.Query().Where(x => + x.EventName == eventKey + && x.EventKey == eventKey + && x.SubscribeAsOf <= asOf + && x.ExternalToken == null + ); + + return await q.FirstOrDefaultAsync(); + } + } + + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + { + try + { + // The query string + var strbuilder = new StringBuilder(); + strbuilder.Append("from EventSubscriptions as e "); + strbuilder.Append($"where e.Id = {eventSubscriptionId} and ExternalToken = null"); + strbuilder.Append("update"); + strbuilder.Append("{"); + strbuilder.Append($"e.ExternalToken = '{token}'"); + strbuilder.Append($"e.ExternalTokenExpiry = '{expiry}'"); + strbuilder.Append($"e.ExternalWorkerId = 'workerId'"); + strbuilder.Append("}"); + + var operation = await _database.Operations.SendAsync(new PatchByQueryOperation(strbuilder.ToString())); + operation.WaitForCompletion(); + return true; + } + catch (Exception e) + { + return false; + } + } + + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + { + try + { + // The query string + var strbuilder = new StringBuilder(); + strbuilder.Append("from EventSubscriptions as e "); + strbuilder.Append($"where e.Id = {eventSubscriptionId} and ExternalToken = '{token}'"); + strbuilder.Append("update"); + strbuilder.Append("{"); + strbuilder.Append($"e.ExternalToken = null"); + strbuilder.Append($"e.ExternalTokenExpiry = null"); + strbuilder.Append($"e.ExternalWorkerId = null"); + strbuilder.Append("}"); + + var operation = await _database.Operations.SendAsync(new PatchByQueryOperation(strbuilder.ToString())); + operation.WaitForCompletion(); + } + catch (Exception e) + { + throw e; + } + } + + public void EnsureStoreExists() { } + + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) + { + using (var session = _database.OpenAsyncSession()) + { + var q = session.Query().Where(x => + x.EventName == eventName + && x.EventKey == eventKey + && x.SubscribeAsOf <= asOf + ); + + return await q.ToListAsync(); + } + } + + public async Task CreateEvent(Event newEvent) + { + using (var session = _database.OpenAsyncSession()) + { + await session.StoreAsync(newEvent); + var id = newEvent.Id; + await session.SaveChangesAsync(); + return id; + } + } + + public async Task GetEvent(string id) + { + using (var session = _database.OpenAsyncSession()) + { + var result = session.Query().Where(x => x.Id == id); + return await result.FirstAsync(); + } + } + + public async Task> GetRunnableEvents(DateTime asAt) + { + using (var session = _database.OpenAsyncSession()) + { + var now = asAt.ToUniversalTime(); + var result = session.Query() + .Where(x => !x.IsProcessed && x.EventTime < now) + .Select(x => x.Id); + + return await result.ToListAsync(); + } + } + + public async Task MarkEventProcessed(string id) + { + using (var session = _database.OpenAsyncSession()) + { + session.Advanced.Patch(id, x => x.IsProcessed, true); + + await session.SaveChangesAsync(); + } + } + + public async Task> GetEvents(string eventName, string eventKey, DateTime asOf) + { + using (var session = _database.OpenAsyncSession()) + { + var q = session.Query() + .Where(x => + x.EventName == eventName + && x.EventKey == eventKey + && x.EventTime >= asOf) + .Select(x => x.Id); + + return await q.ToListAsync(); + } + } + + public async Task MarkEventUnprocessed(string id) + { + using (var session = _database.OpenAsyncSession()) + { + session.Advanced.Patch(id, x => x.IsProcessed, false); + + await session.SaveChangesAsync(); + } + } + + public async Task PersistErrors(IEnumerable errors) + { + if (errors.Any()) + { + var blk = _database.BulkInsert(); + foreach (var error in errors) + await blk.StoreAsync(error); + + } + } + + } +} diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs b/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs new file mode 100644 index 000000000..70fd0210b --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Raven.Client; +using System.Threading.Tasks; +using Raven.Client.Documents; +using System.Linq; +using Raven.Client.Extensions; +using Raven.Client.Documents.Operations; +using Raven.Client.Documents.Queries; + +namespace WorkflowCore.Persistence.RavenDB.Services +{ + public class WorkflowPurger : IWorkflowPurger + { + private readonly IDocumentStore _database; + + public WorkflowPurger(IDocumentStore database) + { + _database = database; + } + + public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan) + { + await DeleteWorkflowInstances(status, olderThan); + } + + + /// + /// Delete all Workflow Documents + /// + /// + private Task DeleteWorkflowInstances(WorkflowStatus status, DateTime olderThan) + { + var utcTime = olderThan.ToUniversalTime(); + var queryToDelete = new IndexQuery { Query = $"FROM {nameof(WorkflowInstance)} where status = '{status}' and CompleteTime < '{olderThan}'" }; + return _database.Operations.SendAsync(new DeleteByQueryOperation(queryToDelete, new QueryOperationOptions { AllowStale = false })); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/WorkflowCore.Persistence.RavenDB.csproj b/src/providers/WorkflowCore.Persistence.RavenDB/WorkflowCore.Persistence.RavenDB.csproj new file mode 100644 index 000000000..69c8e94e7 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.RavenDB/WorkflowCore.Persistence.RavenDB.csproj @@ -0,0 +1,32 @@ + + + + Workflow Core RavenDB Persistence Provider + netstandard2.0 + WorkflowCore.Persistence.RavenDB + WorkflowCore.Persistence.RavenDB + workflow;.NET;Core;state machine;WorkflowCore;RavenDB + https://github.com/danielgerlag/workflow-core + https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md + git + https://github.com/danielgerlag/workflow-core.git + false + false + false + Provides support to persist workflows running on Workflow Core to a RavenDB database. + + + + + + + + + + + + + + + + From a30b7d9ab330418bf3b651ba86d137412391854b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 17 Dec 2020 16:48:25 -0800 Subject: [PATCH 258/462] use hashset for in-memory lock store --- .../Services/DefaultProviders/SingleNodeLockProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WorkflowCore/Services/DefaultProviders/SingleNodeLockProvider.cs b/src/WorkflowCore/Services/DefaultProviders/SingleNodeLockProvider.cs index 3d6107d66..6e920b712 100644 --- a/src/WorkflowCore/Services/DefaultProviders/SingleNodeLockProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/SingleNodeLockProvider.cs @@ -12,7 +12,7 @@ namespace WorkflowCore.Services /// public class SingleNodeLockProvider : IDistributedLockProvider { - private List _locks = new List(); + private HashSet _locks = new HashSet(); public async Task AcquireLock(string Id, CancellationToken cancellationToken) { From 647d9b8dfbd8cbd14a3dce02c9358a0bf8efe502 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 19 Dec 2020 08:47:13 -0800 Subject: [PATCH 259/462] Revert "Add dotnet 5 support (with fixed mysql)" --- ...rkflowCore.Persistence.EntityFramework.csproj | 14 +++++++------- .../WorkflowCore.Persistence.MySQL.csproj | 12 ++++++------ .../WorkflowCore.Persistence.PostgreSQL.csproj | 16 ++++++++-------- .../SqlServerContext.cs | 12 ++++++------ .../WorkflowCore.Persistence.SqlServer.csproj | 16 ++++++++-------- .../WorkflowCore.Persistence.Sqlite.csproj | 12 ++++++------ .../WorkflowCore.QueueProviders.SqlServer.csproj | 4 ++-- .../WorkflowCore.Sample01.csproj | 14 +++++++------- .../WorkflowCore.Sample02.csproj | 6 +++--- .../WorkflowCore.Sample03.csproj | 8 ++++---- .../WorkflowCore.Sample04.csproj | 10 +++++----- .../WorkflowCore.Sample05.csproj | 2 +- .../WorkflowCore.Sample06.csproj | 2 +- .../WorkflowCore.Sample07.csproj | 4 ++-- .../WorkflowCore.Sample08.csproj | 4 ++-- .../WorkflowCore.Sample09.csproj | 4 ++-- .../WorkflowCore.Sample09s.csproj | 6 +++--- .../WorkflowCore.Sample10.csproj | 4 ++-- .../WorkflowCore.Sample11.csproj | 6 +++--- .../WorkflowCore.Sample12.csproj | 4 ++-- .../WorkflowCore.Sample13.csproj | 4 ++-- .../WorkflowCore.Sample14.csproj | 2 +- .../WorkflowCore.Sample15.csproj | 6 +++--- .../WorkflowCore.Sample16.csproj | 6 +++--- .../WorkflowCore.Sample17.csproj | 4 ++-- .../WorkflowCore.Sample18.csproj | 12 ++++++------ .../WorkflowCore.Sample19.csproj | 12 ++++++------ .../WorkflowCore.TestSample01.csproj | 4 ++-- test/ScratchPad/ScratchPad.csproj | 6 +++--- .../WorkflowCore.IntegrationTests.csproj | 8 ++++---- .../WorkflowCore.TestAssets.csproj | 2 +- .../WorkflowCore.Testing.csproj | 12 ++++++------ .../WorkflowCore.Tests.DynamoDB.csproj | 2 +- .../WorkflowCore.Tests.Elasticsearch.csproj | 2 +- .../WorkflowCore.Tests.MongoDB.csproj | 8 ++++---- .../WorkflowCore.Tests.MySQL.csproj | 4 ++-- .../WorkflowCore.Tests.PostgreSQL.csproj | 4 ++-- .../WorkflowCore.Tests.Redis.csproj | 4 ++-- .../WorkflowCore.Tests.SqlServer.csproj | 2 +- .../WorkflowCore.Tests.Sqlite.csproj | 2 +- .../WorkflowCore.UnitTests.csproj | 8 ++++---- 41 files changed, 137 insertions(+), 137 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index a08f951d9..ed8392a62 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -3,7 +3,7 @@ Workflow Core EntityFramework Core Persistence Provider Daniel Gerlag - netstandard2.1 + netstandard2.0 WorkflowCore.Persistence.EntityFramework WorkflowCore.Persistence.EntityFramework workflow;.NET;Core;state machine;WorkflowCore;EntityFramework;EntityFrameworkCore @@ -14,11 +14,11 @@ false false false - 3.3.3 + 3.1.0 Base package for Workflow-core peristence providers using entity framework - 3.3.3.0 - 3.3.3.0 - 3.3.3 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -26,8 +26,8 @@ - - + + diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index f40289070..a031e5fba 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -4,7 +4,7 @@ Workflow Core MySQL Persistence Provider 1.0.0 Daniel Gerlag - netstandard2.1 + netstandard2.0 WorkflowCore.Persistence.MySQL WorkflowCore.Persistence.MySQL workflow;.NET;Core;state machine;WorkflowCore;MySQL @@ -16,17 +16,17 @@ false false Provides support to persist workflows running on Workflow Core to a MySQL database. - 3.3.3 - 3.3.3.0 - 3.3.3.0 + 3.0.1 + 3.0.1.0 + 3.0.1.0 - + all runtime; build; native; contentfiles; analyzers - + diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index a5faadf44..7addc52a4 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,10 +15,10 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 3.3.3 - 3.3.3.0 - 3.3.3.0 - 3.3.3 + 3.1.0 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -27,12 +27,12 @@ - - - + + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs index 7383cce3e..499c10295 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs @@ -28,37 +28,37 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void ConfigureSubscriptionStorage(EntityTypeBuilder builder) { builder.ToTable("Subscription", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } protected override void ConfigureWorkflowStorage(EntityTypeBuilder builder) { builder.ToTable("Workflow", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } protected override void ConfigureExecutionPointerStorage(EntityTypeBuilder builder) { builder.ToTable("ExecutionPointer", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } protected override void ConfigureExecutionErrorStorage(EntityTypeBuilder builder) { builder.ToTable("ExecutionError", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } protected override void ConfigureExetensionAttributeStorage(EntityTypeBuilder builder) { builder.ToTable("ExtensionAttribute", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } protected override void ConfigureEventStorage(EntityTypeBuilder builder) { builder.ToTable("Event", "wfc"); - builder.Property(x => x.PersistenceId).UseIdentityColumn(); + builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); } } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 835f26433..be52279d1 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -4,7 +4,7 @@ Workflow Core SQL Server Persistence Provider 1.8.0 Daniel Gerlag - netstandard2.1 + netstandard2.0 WorkflowCore.Persistence.SqlServer WorkflowCore.Persistence.SqlServer workflow;.NET;Core;state machine;WorkflowCore @@ -15,11 +15,11 @@ false false false - 3.3.3 + 3.1.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 3.3.3.0 - 3.3.3.0 - 3.3.3 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -28,11 +28,11 @@ - - + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 44e1e344e..2d2789021 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -4,7 +4,7 @@ Workflow Core Sqlite Persistence Provider 1.5.0 Daniel Gerlag - netstandard2.1 + netstandard2.0 WorkflowCore.Persistence.Sqlite WorkflowCore.Persistence.Sqlite workflow;.NET;Core;state machine;WorkflowCore;Sqlite @@ -16,10 +16,10 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 3.3.3 - 3.3.3.0 - 3.3.3.0 - 3.3.3 + 3.1.0 + 3.1.0.0 + 3.1.0.0 + 3.1.0 @@ -28,7 +28,7 @@ - + diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 97b48d220..36d7cda77 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -5,7 +5,7 @@ Roberto Paterlini Queue provider for Workflow-core using SQL Server Service Broker - 1.0.4-alpha + 1.0.3-alpha @@ -20,7 +20,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj index 0e878eb0b..e4a09e491 100644 --- a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj +++ b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 WorkflowCore.Sample01 Exe WorkflowCore.Sample01 @@ -16,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj index 7fab128b6..0765d50c3 100644 --- a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj +++ b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 WorkflowCore.Sample02 Exe WorkflowCore.Sample02 @@ -11,8 +11,8 @@ - - + + diff --git a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj index 7b332dcd9..fc4a5d4fd 100644 --- a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj +++ b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.0 WorkflowCore.Sample03 Exe WorkflowCore.Sample03 @@ -16,9 +16,9 @@ - - - + + + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index af988640d..82107b0e6 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0 WorkflowCore.Sample04 Exe WorkflowCore.Sample04 @@ -25,13 +25,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj index 069931ef0..592fa228b 100644 --- a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj +++ b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj index d4c71d9aa..b61478b9e 100644 --- a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj +++ b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index 2f5937f36..5ca51a866 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0 true WorkflowCore.Sample07 Exe @@ -26,7 +26,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj index 2a556e45d..3ff429e28 100644 --- a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj +++ b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0 WorkflowCore.Sample08 Exe WorkflowCore.Sample08 @@ -21,7 +21,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj index bc16eb974..6f75a0d7d 100644 --- a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj +++ b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + netcoreapp3.0 - + diff --git a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj index bc16eb974..e569ec895 100644 --- a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj +++ b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj @@ -1,12 +1,12 @@ - + Exe - netcoreapp3.1 + netcoreapp3.0 - + diff --git a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj index bc16eb974..6f75a0d7d 100644 --- a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj +++ b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + netcoreapp3.0 - + diff --git a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj index 5a8923eeb..b6454f6a2 100644 --- a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj +++ b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp3.1 + netcoreapp2.2 - - + + diff --git a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj index 5b030fb75..9879baab2 100644 --- a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj +++ b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + netcoreapp3.0 - + diff --git a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj index 7f814f488..dbe49166e 100644 --- a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj +++ b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + netcoreapp2.2 - + diff --git a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj index 974472a25..fef8173cc 100644 --- a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj +++ b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + netcoreapp2.2 diff --git a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj index 79ed2ba57..c63fbd792 100644 --- a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj +++ b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp3.1 + netcoreapp2.2 - - + + diff --git a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj index 79ed2ba57..c63fbd792 100644 --- a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj +++ b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp3.1 + netcoreapp2.2 - - + + diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index 766fc8e0c..26933096d 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + netcoreapp3.0 - + diff --git a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj index 5f9b64824..660b47511 100644 --- a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj +++ b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj @@ -1,15 +1,15 @@ - + Exe - netcoreapp3.1 + netcoreapp3.0 - - - - + + + + diff --git a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj index 0f169bcc8..b2a7796c3 100644 --- a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj +++ b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj @@ -1,15 +1,15 @@ - + Exe - netcoreapp3.1 + netcoreapp3.0 - - - - + + + + diff --git a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj index 71454dc11..282fdbb15 100644 --- a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj +++ b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj @@ -1,12 +1,12 @@  - netcoreapp3.1 + netcoreapp2.2 - + diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 4da4684ed..4fb61dd24 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0 ScratchPad Exe ScratchPad @@ -11,8 +11,8 @@ - - + + diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index f7634e8a9..971f10c57 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 WorkflowCore.IntegrationTests WorkflowCore.IntegrationTests true @@ -21,10 +21,10 @@ - - + + - + diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index aeeb3d614..12da1f57b 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + netstandard2.0 WorkflowCore.TestAssets WorkflowCore.TestAssets false diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 22b68fb2f..32f9c1363 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,16 +2,16 @@ netstandard2.0 - 2.3.1 - 2.3.1.0 - 2.3.1.0 + 2.3.0 + 2.3.0.0 + 2.3.0.0 Facilitates testing of workflows built on Workflow-Core - - - + + + diff --git a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index f275a05cf..c2563b8b2 100644 --- a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + netcoreapp2.2 false diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 0ed01e311..168be514f 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 false diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index 1523f31ea..bec4d8561 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 WorkflowCore.Tests.MongoDB WorkflowCore.Tests.MongoDB true @@ -22,9 +22,9 @@ - - - + + + diff --git a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj index 66a792fe5..a255e01d6 100644 --- a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj +++ b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.1 + netcoreapp2.2 false diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 2fefbdea8..1cc7d56bd 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0 WorkflowCore.Tests.PostgreSQL WorkflowCore.Tests.PostgreSQL true @@ -21,7 +21,7 @@ - + diff --git a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj index 76857bc6d..c5ee9a6f8 100644 --- a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj +++ b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.1 + netcoreapp2.2 false diff --git a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj index 497293aaf..011714983 100644 --- a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj +++ b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 diff --git a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj index 9598398f4..9ce1b77cd 100644 --- a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj +++ b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index 5ec1e2ff1..294adbd23 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp2.2 WorkflowCore.UnitTests WorkflowCore.UnitTests true @@ -20,9 +20,9 @@ - - - + + + From c1f67abef0ef4570e57c83a51222c0fbc061dd64 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 19 Dec 2020 08:50:17 -0800 Subject: [PATCH 260/462] issue 682 --- .../ErrorHandlers/CompensateHandler.cs | 14 ++- .../MultistepCompensationScenario2.cs | 97 +++++++++++++++++++ 2 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario2.cs diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index 2023a59f1..dfc13af36 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -29,7 +29,8 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP { var scope = new Stack(exceptionPointer.Scope.Reverse()); scope.Push(exceptionPointer.Id); - + ExecutionPointer compensationPointer = null; + while (scope.Any()) { var pointerId = scope.Pop(); @@ -63,13 +64,18 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP scopePointer.EndTime = _datetimeProvider.UtcNow; scopePointer.Status = PointerStatus.Failed; - ExecutionPointer compensationPointer = null; - if (scopeStep.CompensationStepId.HasValue) { scopePointer.Status = PointerStatus.Compensated; - compensationPointer = _pointerFactory.BuildCompensationPointer(def, scopePointer, exceptionPointer, scopeStep.CompensationStepId.Value); + var nextCompensationPointer = _pointerFactory.BuildCompensationPointer(def, scopePointer, exceptionPointer, scopeStep.CompensationStepId.Value); + if (compensationPointer != null) + { + nextCompensationPointer.Active = false; + nextCompensationPointer.Status = PointerStatus.PendingPredecessor; + nextCompensationPointer.PredecessorId = compensationPointer.Id; + } + compensationPointer = nextCompensationPointer; workflow.ExecutionPointers.Add(compensationPointer); if (resume) diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario2.cs b/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario2.cs new file mode 100644 index 000000000..ec0888501 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario2.cs @@ -0,0 +1,97 @@ +using System; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class MultistepCompensationScenario2 : WorkflowTest + { + public class Workflow : IWorkflow + { + public static int CompensationCounter = 0; + public static int Compensation1_1Fired = 0; + public static int Compensation1_2Fired = 0; + public static int Compensation2_1Fired = 0; + public static int Compensation2_2Fired = 0; + public static int Compensation3Fired = 0; + public static int Compensation4Fired = 0; + + + public string Id => "MultistepCompensationScenario2"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .Saga(x => x + .StartWith(context => ExecutionResult.Next()) + .CompensateWithSequence(context => context.StartWith(c => + { + CompensationCounter++; + Compensation1_1Fired = CompensationCounter; + }) + .Then(c => + { + CompensationCounter++; + Compensation1_2Fired = CompensationCounter; + })) + .If(c => true).Do(then => then + .Then(context => ExecutionResult.Next()) + .CompensateWithSequence(context => context.StartWith(c => + { + CompensationCounter++; + Compensation2_1Fired = CompensationCounter; + }).Then(c => + { + CompensationCounter++; + Compensation2_2Fired = CompensationCounter; + })) + .Then(context => ExecutionResult.Next()) + .CompensateWith(context => + { + CompensationCounter++; + Compensation3Fired = CompensationCounter; + }) + .Then(context => throw new Exception()) + .CompensateWith(context => + { + CompensationCounter++; + Compensation4Fired = CompensationCounter; + })) + ); + } + } + + public MultistepCompensationScenario2() + { + Setup(); + Workflow.Compensation1_1Fired = -1; + Workflow.Compensation1_2Fired = -1; + Workflow.Compensation2_1Fired = -1; + Workflow.Compensation2_2Fired = -1; + Workflow.Compensation3Fired = -1; + Workflow.Compensation4Fired = -1; + Workflow.CompensationCounter = 0; + } + + [Fact] + public void MultiCompensationStepOrder() + { + var workflowId = StartWorkflow(null); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(1); + + Workflow.Compensation1_2Fired.Should().Be(6); + Workflow.Compensation1_1Fired.Should().Be(5); + Workflow.Compensation2_2Fired.Should().Be(4); + Workflow.Compensation2_1Fired.Should().Be(3); + Workflow.Compensation3Fired.Should().Be(2); + Workflow.Compensation4Fired.Should().Be(1); + } + } +} From 0e43c75d9779369482bb2d0ed239fee947089975 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 19 Dec 2020 08:58:53 -0800 Subject: [PATCH 261/462] bump version --- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 36e9f45c1..24da88bcc 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.3 - 3.3.3.0 - 3.3.3.0 + 3.3.4 + 3.3.4.0 + 3.3.4.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.3 + 3.3.4 From f52d8fd929d9dc0cdfbf1a576b47b933bca8f5fe Mon Sep 17 00:00:00 2001 From: Xavid Ramirez Date: Mon, 28 Dec 2020 21:53:34 -0600 Subject: [PATCH 262/462] adding readme to project --- .../README.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/providers/WorkflowCore.Persistence.RavenDB/README.md diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/README.md b/src/providers/WorkflowCore.Persistence.RavenDB/README.md new file mode 100644 index 000000000..fc4835e49 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.RavenDB/README.md @@ -0,0 +1,31 @@ + +# RavenDB Persistence provider for Workflow Core + +Provides support to persist workflows running on [Workflow Core](../../README.md) to a RavenDB database. + +## Installing + +Install the NuGet package "WorkflowCore.Persistence.RavenDB" + +``` +PM> Install-Package WorkflowCore.Persistence.RavenDB +``` + +## Usage + +Compose your RavenStoreOptions using the model provided by the library. + +```C# +var options = new RavenStoreOptions { + ServerUrl = "https://ravendbserver.domain.com:8080", + DatabaseName = "TestDatabase", + CertificatePath = System.IO.Path.Combine(AppContext.BaseDirectory, "Resources/servercert.pfx"), + CertificatePassword = "CertificatePassword" +} +``` + +Use the `.UseRavenDB` extension method when building your service provider, passing in the options you configured for the RavenDB store. + +```C# +services.AddWorkflow(x => x.UseRavenDB(options)); +``` From ccdddf3aaf0ef29f301be142d5208f794d0a68c5 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 29 Dec 2020 18:20:10 -0800 Subject: [PATCH 263/462] Create dotnet.yml --- .github/workflows/dotnet.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/dotnet.yml diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 000000000..d686bebf7 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,25 @@ +name: .NET + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 3.1.301 + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal From a59e4db74a7be3f2da8d3c6ff41b67ff176f35cd Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 29 Dec 2020 18:33:38 -0800 Subject: [PATCH 264/462] Update dotnet.yml --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index d686bebf7..01e08fb1b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -9,7 +9,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: windows-latest steps: - uses: actions/checkout@v2 From 6c5fea10f23609e1cbaf49c7f4eb489629ed41d9 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 29 Dec 2020 18:39:59 -0800 Subject: [PATCH 265/462] github actions --- .github/workflows/dotnet.yml | 2 +- test/ScratchPad/Properties/Resources.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 01e08fb1b..d686bebf7 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -9,7 +9,7 @@ on: jobs: build: - runs-on: windows-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/test/ScratchPad/Properties/Resources.resx b/test/ScratchPad/Properties/Resources.resx index 7a5a7544d..421b272c5 100644 --- a/test/ScratchPad/Properties/Resources.resx +++ b/test/ScratchPad/Properties/Resources.resx @@ -119,6 +119,6 @@ - ..\helloworld.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + ..\HelloWorld.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 \ No newline at end of file From 3e175e0d8bcca35b778450657a809d4899a8d25a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 29 Dec 2020 18:47:00 -0800 Subject: [PATCH 266/462] update test project framework --- .../WorkflowCore.IntegrationTests.csproj | 2 +- .../WorkflowCore.Tests.DynamoDB.csproj | 2 +- .../WorkflowCore.Tests.Elasticsearch.csproj | 2 +- .../WorkflowCore.Tests.MongoDB.csproj | 2 +- test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj | 2 +- .../WorkflowCore.Tests.PostgreSQL.csproj | 2 +- test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj | 2 +- .../WorkflowCore.Tests.SqlServer.csproj | 2 +- test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj | 2 +- test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index 971f10c57..f52ee0ba1 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.IntegrationTests WorkflowCore.IntegrationTests true diff --git a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index c2563b8b2..f275a05cf 100644 --- a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -1,7 +1,7 @@ - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 168be514f..0ed01e311 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index bec4d8561..a76f2b513 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.Tests.MongoDB WorkflowCore.Tests.MongoDB true diff --git a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj index a255e01d6..ff53c72b2 100644 --- a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj +++ b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj @@ -1,7 +1,7 @@ - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 1cc7d56bd..c58b3243f 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 WorkflowCore.Tests.PostgreSQL WorkflowCore.Tests.PostgreSQL true diff --git a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj index c5ee9a6f8..af4f4117d 100644 --- a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj +++ b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj @@ -1,7 +1,7 @@ - netcoreapp2.2 + netcoreapp3.1 false diff --git a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj index 011714983..497293aaf 100644 --- a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj +++ b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 diff --git a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj index 9ce1b77cd..9598398f4 100644 --- a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj +++ b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index 294adbd23..81ceeb3b1 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 WorkflowCore.UnitTests WorkflowCore.UnitTests true From 956632b5836b452c189e6778183da01819c2e8cc Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 29 Dec 2020 19:06:31 -0800 Subject: [PATCH 267/462] tests --- .../SearchIndexTests.cs | 1 + .../ElasticsearchDockerSetup.cs | 2 +- .../Scenarios/MongoDynamicDataScenario.cs | 16 ---------------- 3 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDynamicDataScenario.cs diff --git a/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs b/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs index 8c834f82f..d244ff7c3 100644 --- a/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs +++ b/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs @@ -23,6 +23,7 @@ protected SearchIndexTests() foreach (var item in BuildTestData()) Subject.IndexWorkflow(item).Wait(); + System.Threading.Thread.Sleep(1000); } protected IEnumerable BuildTestData() diff --git a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs index e79779466..aef37beaa 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs +++ b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs @@ -43,7 +43,7 @@ public override bool TestReady() } [CollectionDefinition("Elasticsearch collection")] - public class DynamoDbCollection : ICollectionFixture + public class ElasticsearchCollection : ICollectionFixture { } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDynamicDataScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDynamicDataScenario.cs deleted file mode 100644 index 456f0a0f1..000000000 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDynamicDataScenario.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using WorkflowCore.IntegrationTests.Scenarios; -using Xunit; - -namespace WorkflowCore.Tests.MongoDB.Scenarios -{ - [Collection("Mongo collection")] - public class MongoDynamicDataScenario : DynamicDataIOScenario - { - protected override void ConfigureServices(IServiceCollection services) - { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); - } - } -} From b72e2b038fb81718bfe474a08db03d7004a41771 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 30 Dec 2020 07:20:41 -0800 Subject: [PATCH 268/462] test setup --- test/Docker.Testify/DockerSetup.cs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/test/Docker.Testify/DockerSetup.cs b/test/Docker.Testify/DockerSetup.cs index 0bf7055df..f5dc0423a 100644 --- a/test/Docker.Testify/DockerSetup.cs +++ b/test/Docker.Testify/DockerSetup.cs @@ -30,6 +30,8 @@ public abstract class DockerSetup : IDisposable protected readonly DockerClient docker; protected string containerId; + private static object Lock = new object(); + protected DockerSetup() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -122,21 +124,24 @@ public void Dispose() private int GetFreePort() { - const int startRange = 1000; - const int endRange = 10000; - var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); - var tcpPorts = ipGlobalProperties.GetActiveTcpListeners(); - var udpPorts = ipGlobalProperties.GetActiveUdpListeners(); + lock (Lock) + { + const int startRange = 1000; + const int endRange = 10000; + var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); + var tcpPorts = ipGlobalProperties.GetActiveTcpListeners(); + var udpPorts = ipGlobalProperties.GetActiveUdpListeners(); - var result = startRange; + var result = startRange; - while (((tcpPorts.Any(x => x.Port == result)) || (udpPorts.Any(x => x.Port == result))) && result <= endRange) - result++; + while (((tcpPorts.Any(x => x.Port == result)) || (udpPorts.Any(x => x.Port == result))) && result <= endRange) + result++; - if (result > endRange) - throw new PortsInUseException(); + if (result > endRange) + throw new PortsInUseException(); - return result; + return result; + } } } } From 301466b05808c2040bd774defdad22eefb78729a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 30 Dec 2020 07:31:18 -0800 Subject: [PATCH 269/462] test setup --- .../WorkflowCore.TestSample01.csproj | 2 +- test/Docker.Testify/DockerSetup.cs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj index 282fdbb15..46df42954 100644 --- a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj +++ b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 diff --git a/test/Docker.Testify/DockerSetup.cs b/test/Docker.Testify/DockerSetup.cs index f5dc0423a..278567231 100644 --- a/test/Docker.Testify/DockerSetup.cs +++ b/test/Docker.Testify/DockerSetup.cs @@ -30,7 +30,7 @@ public abstract class DockerSetup : IDisposable protected readonly DockerClient docker; protected string containerId; - private static object Lock = new object(); + private static HashSet UsedPorts = new HashSet(); protected DockerSetup() { @@ -124,7 +124,7 @@ public void Dispose() private int GetFreePort() { - lock (Lock) + lock (UsedPorts) { const int startRange = 1000; const int endRange = 10000; @@ -134,11 +134,13 @@ private int GetFreePort() var result = startRange; - while (((tcpPorts.Any(x => x.Port == result)) || (udpPorts.Any(x => x.Port == result))) && result <= endRange) + while (((tcpPorts.Any(x => x.Port == result)) || (udpPorts.Any(x => x.Port == result))) && result <= endRange && !UsedPorts.Contains(result)) result++; if (result > endRange) throw new PortsInUseException(); + + UsedPorts.Add(result); return result; } From 4b8fbb9cd106b594965e1cbb88c56c9c0941b311 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 30 Dec 2020 08:02:13 -0800 Subject: [PATCH 270/462] docker test timeout --- test/Docker.Testify/DockerSetup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Docker.Testify/DockerSetup.cs b/test/Docker.Testify/DockerSetup.cs index 278567231..8763d8855 100644 --- a/test/Docker.Testify/DockerSetup.cs +++ b/test/Docker.Testify/DockerSetup.cs @@ -20,7 +20,7 @@ public abstract class DockerSetup : IDisposable public abstract int InternalPort { get; } public virtual string ImageTag => "latest"; - public virtual TimeSpan TimeOut => TimeSpan.FromSeconds(30); + public virtual TimeSpan TimeOut => TimeSpan.FromSeconds(60); public virtual IList EnvironmentVariables => new List(); public int ExternalPort { get; } From 2069c15258c8dfaff0da457d73fcc87df2d4ca13 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 30 Dec 2020 08:13:09 -0800 Subject: [PATCH 271/462] test setup --- test/Docker.Testify/DockerSetup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Docker.Testify/DockerSetup.cs b/test/Docker.Testify/DockerSetup.cs index 8763d8855..7d5ae9bc3 100644 --- a/test/Docker.Testify/DockerSetup.cs +++ b/test/Docker.Testify/DockerSetup.cs @@ -126,8 +126,8 @@ private int GetFreePort() { lock (UsedPorts) { - const int startRange = 1000; - const int endRange = 10000; + const int startRange = 10002; + const int endRange = 15000; var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); var tcpPorts = ipGlobalProperties.GetActiveTcpListeners(); var udpPorts = ipGlobalProperties.GetActiveUdpListeners(); From 47cb5d0b88e2f7ff3bc35e418a4bcd3353b8931e Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 30 Dec 2020 08:50:45 -0800 Subject: [PATCH 272/462] dynamodb tests docker setup timeout --- test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs b/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs index a166ec804..6e52c238e 100644 --- a/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs +++ b/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs @@ -20,6 +20,7 @@ public class DynamoDbDockerSetup : DockerSetup public override string ImageName => @"amazon/dynamodb-local"; public override int InternalPort => 8000; + public override TimeSpan TimeOut => TimeSpan.FromSeconds(120); public override void PublishConnectionInfo() { From 168cabab85763c1aef8a952d534f4d780c72d872 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 30 Dec 2020 09:08:12 -0800 Subject: [PATCH 273/462] Update dotnet.yml --- .github/workflows/dotnet.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index d686bebf7..29100399e 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -21,5 +21,15 @@ jobs: run: dotnet restore - name: Build run: dotnet build --no-restore - - name: Test - run: dotnet test --no-build --verbosity normal + - name: Unit Tests + run: dotnet test test/WorkflowCore.UnitTests --no-build --verbosity normal + - name: Integration Tests + run: dotnet test test/WorkflowCore.IntegrationTests --no-build --verbosity normal + - name: PostgreSQL Tests + run: dotnet test test/WorkflowCore.Tests.PostgreSQL --no-build --verbosity normal + - name: Redis Tests + run: dotnet test test/WorkflowCore.Tests.Redis --no-build --verbosity normal + - name: SQL Server Tests + run: dotnet test test/WorkflowCore.Tests.SqlServer --no-build --verbosity normal + - name: Elasticsearch Tests + run: dotnet test test/WorkflowCore.Tests.Elasticsearch --no-build --verbosity normal From 1118bad9eed70d850ded700635a191a5e1c1e652 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 31 Dec 2020 09:15:37 -0800 Subject: [PATCH 274/462] bump versions --- src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 3 ++- src/WorkflowCore/WorkflowCore.csproj | 9 ++++----- .../Services/WorkflowPurger.cs | 1 - .../Services/DataObjectSerializer.cs | 1 - 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index 3670d6553..689d95119 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 3.3.0 + 3.3.1 DSL extenstion for Workflow Core provding support for JSON and YAML workflow definitions. Daniel Gerlag @@ -15,6 +15,7 @@ + diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 24da88bcc..2f465db0d 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.4 - 3.3.4.0 - 3.3.4.0 + 3.3.5 + 3.3.5.0 + 3.3.5.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.4 + 3.3.5 @@ -30,7 +30,6 @@ - diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs index e75caaed0..31384be84 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Linq.Dynamic.Core; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using WorkflowCore.Interface; diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs index a01a770d9..894b58ed1 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Dynamic.Core; using System.Linq.Expressions; using System.Threading.Tasks; using WorkflowCore.Models; From ad4b6dbd50802c24fbde95d9f07de6b56d9a3176 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 31 Dec 2020 09:17:42 -0800 Subject: [PATCH 275/462] bump versions --- .../WorkflowCore.Persistence.EntityFramework.csproj | 8 ++++---- .../WorkflowCore.Persistence.MongoDB.csproj | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index ed8392a62..b126a9c23 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -14,11 +14,11 @@ false false false - 3.1.0 + 3.1.1 Base package for Workflow-core peristence providers using entity framework - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.1.1.0 + 3.1.1.0 + 3.1.1 diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index b762cb930..b6e6204c0 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -14,11 +14,11 @@ false false false - 3.0.2 + 3.0.3 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 3.0.2.0 - 3.0.2.0 - 3.0.2 + 3.0.3.0 + 3.0.3.0 + 3.0.3 From fdbd1f390d206b264eda9074d54bf2d5d4ebc59f Mon Sep 17 00:00:00 2001 From: Fu Yu Date: Wed, 6 Jan 2021 15:45:27 +1100 Subject: [PATCH 276/462] SqlServer Provider: replace UseSqlServerIdentityColumn with UseIdentityColumn --- .../SqlServerContext.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs index 499c10295..7383cce3e 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs @@ -28,37 +28,37 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void ConfigureSubscriptionStorage(EntityTypeBuilder builder) { builder.ToTable("Subscription", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureWorkflowStorage(EntityTypeBuilder builder) { builder.ToTable("Workflow", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureExecutionPointerStorage(EntityTypeBuilder builder) { builder.ToTable("ExecutionPointer", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureExecutionErrorStorage(EntityTypeBuilder builder) { builder.ToTable("ExecutionError", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureExetensionAttributeStorage(EntityTypeBuilder builder) { builder.ToTable("ExtensionAttribute", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } protected override void ConfigureEventStorage(EntityTypeBuilder builder) { builder.ToTable("Event", "wfc"); - builder.Property(x => x.PersistenceId).UseSqlServerIdentityColumn(); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); } } } From 596d167e30a030fd92601dd1765803db0b195863 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 8 Jan 2021 20:51:50 -0800 Subject: [PATCH 277/462] bump version --- .../WorkflowCore.Persistence.SqlServer.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index be52279d1..fe5c32f81 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,11 +15,11 @@ false false false - 3.1.0 + 3.1.1 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.1.1.0 + 3.1.1.0 + 3.1.1 From 68a38035c210423fbd6c9f93d3cb34ce53c97943 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 9 Jan 2021 09:53:06 -0800 Subject: [PATCH 278/462] fixes for break EF core 5 changes --- ...WorkflowCore.Persistence.PostgreSQL.csproj | 16 +- .../20210109172432_EFCore5.Designer.cs | 338 ++++++++++++++++++ .../Migrations/20210109172432_EFCore5.cs | 17 + ...lServerPersistenceProviderModelSnapshot.cs | 138 +++---- .../WorkflowCore.Persistence.SqlServer.csproj | 16 +- .../WorkflowCore.Persistence.Sqlite.csproj | 12 +- .../WorkflowCore.Sample03.csproj | 4 +- .../WorkflowCore.Sample04.csproj | 15 +- .../WorkflowCore.Sample07.csproj | 4 +- .../WorkflowCore.Sample13.csproj | 2 +- .../WorkflowCore.Sample14.csproj | 2 +- .../WorkflowCore.Sample17.csproj | 4 +- test/ScratchPad/ScratchPad.csproj | 2 +- .../WorkflowCore.Tests.PostgreSQL.csproj | 2 +- 14 files changed, 469 insertions(+), 103 deletions(-) create mode 100644 src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20210109172432_EFCore5.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20210109172432_EFCore5.cs diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 7addc52a4..bcebe0247 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,10 +15,10 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 3.1.0 - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.3.0 + 3.3.0.0 + 3.3.0.0 + 3.3.0 @@ -27,12 +27,12 @@ - - - + + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20210109172432_EFCore5.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20210109172432_EFCore5.Designer.cs new file mode 100644 index 000000000..2fe0f62d2 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20210109172432_EFCore5.Designer.cs @@ -0,0 +1,338 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WorkflowCore.Persistence.SqlServer; + +namespace WorkflowCore.Persistence.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerContext))] + [Migration("20210109172432_EFCore5")] + partial class EFCore5 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseIdentityColumns() + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.Property("EventData") + .HasColumnType("nvarchar(max)"); + + b.Property("EventId") + .HasColumnType("uniqueidentifier"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("EventTime") + .HasColumnType("datetime2"); + + b.Property("IsProcessed") + .HasColumnType("bit"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.Property("ErrorTime") + .HasColumnType("datetime2"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Message") + .HasColumnType("nvarchar(max)"); + + b.Property("WorkflowId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Children") + .HasColumnType("nvarchar(max)"); + + b.Property("ContextItem") + .HasColumnType("nvarchar(max)"); + + b.Property("EndTime") + .HasColumnType("datetime2"); + + b.Property("EventData") + .HasColumnType("nvarchar(max)"); + + b.Property("EventKey") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("EventName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("EventPublished") + .HasColumnType("bit"); + + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Outcome") + .HasColumnType("nvarchar(max)"); + + b.Property("PersistenceData") + .HasColumnType("nvarchar(max)"); + + b.Property("PredecessorId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Scope") + .HasColumnType("nvarchar(max)"); + + b.Property("SleepUntil") + .HasColumnType("datetime2"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("StepName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("WorkflowId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.Property("AttributeKey") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("AttributeValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutionPointerId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ExecutionPointerId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ExternalToken") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ExternalTokenExpiry") + .HasColumnType("datetime2"); + + b.Property("ExternalWorkerId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("SubscribeAsOf") + .HasColumnType("datetime2"); + + b.Property("SubscriptionData") + .HasColumnType("nvarchar(max)"); + + b.Property("SubscriptionId") + .HasMaxLength(200) + .HasColumnType("uniqueidentifier"); + + b.Property("WorkflowId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.Property("CompleteTime") + .HasColumnType("datetime2"); + + b.Property("CreateTime") + .HasColumnType("datetime2"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("InstanceId") + .HasMaxLength(200) + .HasColumnType("uniqueidentifier"); + + b.Property("NextExecution") + .HasColumnType("bigint"); + + b.Property("Reference") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Version") + .HasColumnType("int"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Workflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Navigation("ExtensionAttributes"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Navigation("ExecutionPointers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20210109172432_EFCore5.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20210109172432_EFCore5.cs new file mode 100644 index 000000000..e36775325 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20210109172432_EFCore5.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.SqlServer.Migrations +{ + public partial class EFCore5 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs index 8ecddd28e..1b5c63cc1 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs @@ -15,18 +15,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.0") + .UseIdentityColumns() .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("ProductVersion", "5.0.1"); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => { b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .HasAnnotation("SqlServer:IdentityIncrement", 1) - .HasAnnotation("SqlServer:IdentitySeed", 1) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("EventData") .HasColumnType("nvarchar(max)"); @@ -35,12 +33,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uniqueidentifier"); b.Property("EventKey") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("EventName") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("EventTime") .HasColumnType("datetime2"); @@ -59,7 +57,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("EventName", "EventKey"); - b.ToTable("Event","wfc"); + b.ToTable("Event", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => @@ -67,27 +65,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .HasAnnotation("SqlServer:IdentityIncrement", 1) - .HasAnnotation("SqlServer:IdentitySeed", 1) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ErrorTime") .HasColumnType("datetime2"); b.Property("ExecutionPointerId") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("Message") .HasColumnType("nvarchar(max)"); b.Property("WorkflowId") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.HasKey("PersistenceId"); - b.ToTable("ExecutionError","wfc"); + b.ToTable("ExecutionError", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => @@ -95,9 +91,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .HasAnnotation("SqlServer:IdentityIncrement", 1) - .HasAnnotation("SqlServer:IdentitySeed", 1) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("Active") .HasColumnType("bit"); @@ -115,19 +109,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("EventKey") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("EventName") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("EventPublished") .HasColumnType("bit"); b.Property("Id") - .HasColumnType("nvarchar(50)") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); b.Property("Outcome") .HasColumnType("nvarchar(max)"); @@ -136,8 +130,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("PredecessorId") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("RetryCount") .HasColumnType("int"); @@ -158,8 +152,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("int"); b.Property("StepName") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("WorkflowId") .HasColumnType("bigint"); @@ -168,7 +162,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("WorkflowId"); - b.ToTable("ExecutionPointer","wfc"); + b.ToTable("ExecutionPointer", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => @@ -176,13 +170,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .HasAnnotation("SqlServer:IdentityIncrement", 1) - .HasAnnotation("SqlServer:IdentitySeed", 1) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("AttributeKey") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("AttributeValue") .HasColumnType("nvarchar(max)"); @@ -194,7 +186,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ExecutionPointerId"); - b.ToTable("ExtensionAttribute","wfc"); + b.ToTable("ExtensionAttribute", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => @@ -202,32 +194,30 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .HasAnnotation("SqlServer:IdentityIncrement", 1) - .HasAnnotation("SqlServer:IdentitySeed", 1) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("EventKey") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("EventName") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ExecutionPointerId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ExternalToken") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ExternalTokenExpiry") .HasColumnType("datetime2"); b.Property("ExternalWorkerId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("StepId") .HasColumnType("int"); @@ -239,12 +229,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("SubscriptionId") - .HasColumnType("uniqueidentifier") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("uniqueidentifier"); b.Property("WorkflowId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.HasKey("PersistenceId"); @@ -255,7 +245,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("SubscriptionId") .IsUnique(); - b.ToTable("Subscription","wfc"); + b.ToTable("Subscription", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => @@ -263,9 +253,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .HasAnnotation("SqlServer:IdentityIncrement", 1) - .HasAnnotation("SqlServer:IdentitySeed", 1) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("CompleteTime") .HasColumnType("datetime2"); @@ -277,19 +265,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Description") - .HasColumnType("nvarchar(500)") - .HasMaxLength(500); + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); b.Property("InstanceId") - .HasColumnType("uniqueidentifier") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("uniqueidentifier"); b.Property("NextExecution") .HasColumnType("bigint"); b.Property("Reference") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Status") .HasColumnType("int"); @@ -298,8 +286,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("int"); b.Property("WorkflowDefinitionId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.HasKey("PersistenceId"); @@ -308,7 +296,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("NextExecution"); - b.ToTable("Workflow","wfc"); + b.ToTable("Workflow", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => @@ -318,6 +306,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("WorkflowId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("Workflow"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => @@ -327,6 +317,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("ExecutionPointerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Navigation("ExtensionAttributes"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Navigation("ExecutionPointers"); }); #pragma warning restore 612, 618 } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index fe5c32f81..f352e79c0 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -4,7 +4,7 @@ Workflow Core SQL Server Persistence Provider 1.8.0 Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.SqlServer WorkflowCore.Persistence.SqlServer workflow;.NET;Core;state machine;WorkflowCore @@ -15,11 +15,11 @@ false false false - 3.1.1 + 3.3.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 3.1.1.0 - 3.1.1.0 - 3.1.1 + 3.3.0.0 + 3.3.0.0 + 3.3.0 @@ -28,11 +28,11 @@ - - + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 2d2789021..faa1eb934 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -4,7 +4,7 @@ Workflow Core Sqlite Persistence Provider 1.5.0 Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.Sqlite WorkflowCore.Persistence.Sqlite workflow;.NET;Core;state machine;WorkflowCore;Sqlite @@ -16,10 +16,10 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 3.1.0 - 3.1.0.0 - 3.1.0.0 - 3.1.0 + 3.3.0 + 3.3.0.0 + 3.3.0.0 + 3.3.0 @@ -28,7 +28,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj index fc4a5d4fd..d211d3d2f 100644 --- a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj +++ b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp3.1 WorkflowCore.Sample03 Exe WorkflowCore.Sample03 @@ -16,7 +16,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index 82107b0e6..96f0355c0 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 WorkflowCore.Sample04 Exe WorkflowCore.Sample04 @@ -25,11 +25,20 @@ - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index 5ca51a866..2f5937f36 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 true WorkflowCore.Sample07 Exe @@ -26,7 +26,7 @@ - + diff --git a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj index dbe49166e..34e1b0433 100644 --- a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj +++ b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.1 diff --git a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj index fef8173cc..974472a25 100644 --- a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj +++ b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.1 diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index 26933096d..766fc8e0c 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - + diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 4fb61dd24..1ceab6240 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + net5.0 ScratchPad Exe ScratchPad diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index c58b3243f..2fefbdea8 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -21,7 +21,7 @@ - + From 2c381556bed3201db0bf4464fb40bd75d743e36e Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 9 Jan 2021 10:43:35 -0800 Subject: [PATCH 279/462] scratchpad project --- test/ScratchPad/ScratchPad.csproj | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 1ceab6240..2b63861b1 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -1,7 +1,7 @@  - net5.0 + netcoreapp3.1 ScratchPad Exe ScratchPad @@ -10,11 +10,6 @@ false - - - - - From 7a908e89e0bf6a739d68c240803c4a7369f1d14c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Jan 2021 09:07:39 -0800 Subject: [PATCH 280/462] use message handler instead of session handler --- .../Services/ServiceBusLifeCycleEventHub.cs | 34 +++++++------------ .../WorkflowCore.Providers.Azure.csproj | 6 ++-- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/ServiceBusLifeCycleEventHub.cs b/src/providers/WorkflowCore.Providers.Azure/Services/ServiceBusLifeCycleEventHub.cs index 13ba2df55..85f7a4245 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/ServiceBusLifeCycleEventHub.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/ServiceBusLifeCycleEventHub.cs @@ -16,14 +16,12 @@ public class ServiceBusLifeCycleEventHub : ILifeCycleEventHub private readonly ITopicClient _topicClient; private readonly ILogger _logger; private readonly ISubscriptionClient _subscriptionClient; - private readonly ICollection> _subscribers = - new HashSet>(); - private readonly JsonSerializerSettings _serializerSettings = - new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All, - ReferenceLoopHandling = ReferenceLoopHandling.Error, - }; + private readonly ICollection> _subscribers = new HashSet>(); + private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All, + ReferenceLoopHandling = ReferenceLoopHandling.Error, + }; public ServiceBusLifeCycleEventHub( string connectionString, @@ -31,8 +29,7 @@ public ServiceBusLifeCycleEventHub( string subscriptionName, ILoggerFactory logFactory) { - _subscriptionClient = new SubscriptionClient( - connectionString, topicName, subscriptionName); + _subscriptionClient = new SubscriptionClient(connectionString, topicName, subscriptionName); _topicClient = new TopicClient(connectionString, topicName); _logger = logFactory.CreateLogger(GetType()); } @@ -55,14 +52,12 @@ public void Subscribe(Action action) public Task Start() { - var sessionHandlerOptions = new SessionHandlerOptions(ExceptionHandler) + var messageHandlerOptions = new MessageHandlerOptions(ExceptionHandler) { - MaxConcurrentSessions = 1, AutoComplete = false }; - _subscriptionClient.RegisterSessionHandler( - MessageHandler, sessionHandlerOptions); + _subscriptionClient.RegisterMessageHandler(MessageHandler, messageHandlerOptions); return Task.CompletedTask; } @@ -73,10 +68,7 @@ public async Task Stop() await _subscriptionClient.CloseAsync(); } - private async Task MessageHandler( - IMessageSession messageSession, - Message message, - CancellationToken cancellationToken) + private async Task MessageHandler(Message message, CancellationToken cancellationToken) { try { @@ -92,15 +84,13 @@ await _subscriptionClient } catch { - await _subscriptionClient - .AbandonAsync(message.SystemProperties.LockToken); + await _subscriptionClient.AbandonAsync(message.SystemProperties.LockToken); } } private Task ExceptionHandler(ExceptionReceivedEventArgs arg) { - _logger.LogWarning( - default, arg.Exception, "Error on receiving events"); + _logger.LogWarning(default, arg.Exception, "Error on receiving events"); return Task.CompletedTask; } diff --git a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj index 43013c80c..cd09e101e 100644 --- a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj +++ b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj @@ -7,15 +7,15 @@ - Provides distributed lock management on Workflow Core - Provides Queueing support on Workflow Core workflow workflowcore dlm - 3.0.0 + 3.1.0 $(PackageTargetFallback);dnxcore50 https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git Daniel Gerlag - 3.0.0.0 - 3.0.0.0 + 3.1.0.0 + 3.1.0.0 From 6ee54cd4aec33e6aa248f4fe4822319a8f5982af Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Jan 2021 09:17:25 -0800 Subject: [PATCH 281/462] cleanup warnings --- .../SqlLockProvider.cs | 11 ++++------- .../Services/AzureLockManager.cs | 6 ++++-- .../Services/RedisPersistenceProvider.cs | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/providers/WorkflowCore.LockProviders.SqlServer/SqlLockProvider.cs b/src/providers/WorkflowCore.LockProviders.SqlServer/SqlLockProvider.cs index 7726d3ebf..cfeafd36c 100644 --- a/src/providers/WorkflowCore.LockProviders.SqlServer/SqlLockProvider.cs +++ b/src/providers/WorkflowCore.LockProviders.SqlServer/SqlLockProvider.cs @@ -133,12 +133,9 @@ public async Task ReleaseLock(string Id) } } - public async Task Start() - { - } - - public async Task Stop() - { - } + public Task Start() => Task.CompletedTask; + + public Task Stop() => Task.CompletedTask; + } } diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs b/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs index 3d718b6e9..9b6d0bdb1 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs @@ -93,13 +93,15 @@ public async Task Start() _renewTimer = new Timer(RenewLeases, null, RenewInterval, RenewInterval); } - public async Task Stop() + public Task Stop() { if (_renewTimer == null) - return; + return Task.CompletedTask; _renewTimer.Dispose(); _renewTimer = null; + + return Task.CompletedTask; } private async void RenewLeases(object state) diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index 103e7c442..3e593a550 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -70,7 +70,7 @@ public async Task> GetRunnableInstances(DateTime asAt) return result; } - public async Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, + public Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) { throw new NotImplementedException(); From e0500f549c5a658f37a8ecbfa95eb2f9552796ca Mon Sep 17 00:00:00 2001 From: xavier Date: Mon, 25 Jan 2021 16:52:53 +0100 Subject: [PATCH 282/462] Extend RunWorkflowSync Extend RunWorkflowSync using a CancelationToken --- src/WorkflowCore/Interface/ISyncWorkflowRunner.cs | 4 ++++ src/WorkflowCore/Services/SyncWorkflowRunner.cs | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs b/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs index 2fe69771b..051b59e91 100644 --- a/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs +++ b/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using WorkflowCore.Models; @@ -8,5 +9,8 @@ public interface ISyncWorkflowRunner { Task RunWorkflowSync(string workflowId, int version, TData data, string reference, TimeSpan timeOut, bool persistSate = true) where TData : new(); + + Task RunWorkflowSync(string workflowId, int version, TData data, string reference, CancellationToken token, bool persistSate = true) + where TData : new(); } } \ No newline at end of file diff --git a/src/WorkflowCore/Services/SyncWorkflowRunner.cs b/src/WorkflowCore/Services/SyncWorkflowRunner.cs index 65bf6c626..3655c2fd0 100644 --- a/src/WorkflowCore/Services/SyncWorkflowRunner.cs +++ b/src/WorkflowCore/Services/SyncWorkflowRunner.cs @@ -32,7 +32,15 @@ public SyncWorkflowRunner(IWorkflowHost host, IWorkflowExecutor executor, IDistr _dateTimeProvider = dateTimeProvider; } - public async Task RunWorkflowSync(string workflowId, int version, TData data, string reference, TimeSpan timeOut, bool persistSate = true) + public Task RunWorkflowSync(string workflowId, int version, TData data, + string reference, TimeSpan timeOut, bool persistSate = true) + where TData : new() + { + return RunWorkflowSync(workflowId, version, data, reference, new CancellationTokenSource(timeOut).Token, + persistSate); + } + + public async Task RunWorkflowSync(string workflowId, int version, TData data, string reference, CancellationToken token, bool persistSate = true) where TData : new() { var def = _registry.GetDefinition(workflowId, version); From 868841bb9f484baa3ee6deb0608bb4c3a678fdd5 Mon Sep 17 00:00:00 2001 From: xavier Date: Mon, 25 Jan 2021 16:52:53 +0100 Subject: [PATCH 283/462] Extend RunWorkflowSync Extend RunWorkflowSync using a CancelationToken --- .../Interface/ISyncWorkflowRunner.cs | 4 ++++ src/WorkflowCore/Services/SyncWorkflowRunner.cs | 16 ++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs b/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs index 2fe69771b..051b59e91 100644 --- a/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs +++ b/src/WorkflowCore/Interface/ISyncWorkflowRunner.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using WorkflowCore.Models; @@ -8,5 +9,8 @@ public interface ISyncWorkflowRunner { Task RunWorkflowSync(string workflowId, int version, TData data, string reference, TimeSpan timeOut, bool persistSate = true) where TData : new(); + + Task RunWorkflowSync(string workflowId, int version, TData data, string reference, CancellationToken token, bool persistSate = true) + where TData : new(); } } \ No newline at end of file diff --git a/src/WorkflowCore/Services/SyncWorkflowRunner.cs b/src/WorkflowCore/Services/SyncWorkflowRunner.cs index 65bf6c626..293c976a0 100644 --- a/src/WorkflowCore/Services/SyncWorkflowRunner.cs +++ b/src/WorkflowCore/Services/SyncWorkflowRunner.cs @@ -32,7 +32,15 @@ public SyncWorkflowRunner(IWorkflowHost host, IWorkflowExecutor executor, IDistr _dateTimeProvider = dateTimeProvider; } - public async Task RunWorkflowSync(string workflowId, int version, TData data, string reference, TimeSpan timeOut, bool persistSate = true) + public Task RunWorkflowSync(string workflowId, int version, TData data, + string reference, TimeSpan timeOut, bool persistSate = true) + where TData : new() + { + return RunWorkflowSync(workflowId, version, data, reference, new CancellationTokenSource(timeOut).Token, + persistSate); + } + + public async Task RunWorkflowSync(string workflowId, int version, TData data, string reference, CancellationToken token, bool persistSate = true) where TData : new() { var def = _registry.GetDefinition(workflowId, version); @@ -63,8 +71,6 @@ public async Task RunWorkflowSync(string workflowId, in wf.ExecutionPointers.Add(_pointerFactory.BuildGenesisPointer(def)); - var stopWatch = new Stopwatch(); - var id = Guid.NewGuid().ToString(); if (persistSate) @@ -81,8 +87,7 @@ public async Task RunWorkflowSync(string workflowId, in try { - stopWatch.Start(); - while ((wf.Status == WorkflowStatus.Runnable) && (timeOut.TotalMilliseconds > stopWatch.ElapsedMilliseconds)) + while ((wf.Status == WorkflowStatus.Runnable) && !token.IsCancellationRequested) { await _executor.Execute(wf); if (persistSate) @@ -91,7 +96,6 @@ public async Task RunWorkflowSync(string workflowId, in } finally { - stopWatch.Stop(); await _lockService.ReleaseLock(id); } From e68d78d4129361ef19920eb135e393318d3ffb60 Mon Sep 17 00:00:00 2001 From: Mike Oldfield Date: Thu, 28 Jan 2021 15:40:37 +0200 Subject: [PATCH 284/462] Refactored DataObjectSerializer to be able to use decimal value type and BsonRepresentation attributes --- .../Services/DataObjectSerializer.cs | 101 ++++++++++++++++-- .../Scenarios/DataIOScenario.cs | 14 ++- .../WorkflowCore.Testing.csproj | 1 + test/WorkflowCore.Testing/WorkflowTest.cs | 8 +- 4 files changed, 112 insertions(+), 12 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs index 894b58ed1..f9d34d004 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs @@ -2,11 +2,9 @@ using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using WorkflowCore.Models; using Newtonsoft.Json; namespace WorkflowCore.Persistence.MongoDB.Services @@ -17,7 +15,7 @@ public class DataObjectSerializer : SerializerBase { TypeNameHandling = TypeNameHandling.Objects, }; - + public override object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { if (context.Reader.CurrentBsonType == BsonType.String) @@ -26,18 +24,103 @@ public override object Deserialize(BsonDeserializationContext context, BsonDeser return JsonConvert.DeserializeObject(raw, SerializerSettings); } - return BsonSerializer.Deserialize(context.Reader, typeof(object)); + var obj = BsonSerializer.Deserialize(context.Reader, typeof(object)); + return obj; } public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { - var str = JsonConvert.SerializeObject(value, SerializerSettings); - var doc = BsonDocument.Parse(str); - ConvertMetaFormat(doc); + BsonDocument doc; + if (BsonClassMap.IsClassMapRegistered(value.GetType())) + { + doc = value.ToBsonDocument(); + doc.Remove("_t"); + doc.InsertAt(0, new BsonElement("_t", value.GetType().AssemblyQualifiedName)); + AddTypeInformation(doc.Elements, value, string.Empty); + } + else + { + var str = JsonConvert.SerializeObject(value, SerializerSettings); + doc = BsonDocument.Parse(str); + ConvertMetaFormat(doc); + } BsonSerializer.Serialize(context.Writer, doc); } + private void AddTypeInformation(IEnumerable elements, object value, string xPath) + { + foreach (var element in elements) + { + var elementXPath = string.IsNullOrEmpty(xPath) ? element.Name : xPath + "." + element.Name; + if (element.Value.IsBsonDocument) + { + var doc = element.Value.AsBsonDocument; + doc.Remove("_t"); + doc.InsertAt(0, new BsonElement("_t", GetTypeNameFromXPath(value, elementXPath))); + AddTypeInformation(doc.Elements, value, elementXPath); + } + if (element.Value.IsBsonArray) + { + AddTypeInformation(element.Value.AsBsonArray, value, elementXPath); + } + } + } + + private string GetTypeNameFromXPath(object root, string xPath) + { + var parts = xPath.Split('.').ToList(); + object value = root; + while (parts.Count > 0) + { + var subPath = parts[0]; + if (subPath[0] == '[') + { + var index = Int32.Parse(subPath.Trim('[', ']')); + if (value.GetType().IsSubclassOf(typeof(IList)) || value.GetType().IsArray) + { + IList list = (IList) value; + value = list[index]; + } + else + { + throw new NotSupportedException(); + } + } + else + { + var propInfo = value.GetType().GetProperty(subPath); + value = propInfo.GetValue(value); + } + + parts.RemoveAt(0); + } + + return value.GetType().AssemblyQualifiedName; + } + + private void AddTypeInformation(IEnumerable elements, object value, string xPath) + { + //foreach (var element in elements) + for (int i = 0; i < elements.Count(); i++) + { + var element = elements.ElementAt(i); + if (element.IsBsonDocument) + { + var doc = element.AsBsonDocument; + var elementXPath = xPath + $".[{i}]"; + doc.Remove("_t"); + doc.InsertAt(0, new BsonElement("_t", GetTypeNameFromXPath(value, elementXPath))); + AddTypeInformation(doc.Elements, value, elementXPath); + } + + if (element.IsBsonArray) + { + AddTypeInformation(element.AsBsonArray, value, xPath); + } + } + } + private static void ConvertMetaFormat(BsonDocument root) { var stack = new Stack(); @@ -72,4 +155,4 @@ private static void ConvertMetaFormat(BsonDocument root) } } } -} +} \ No newline at end of file diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs index 630bcbfe7..0e460defd 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs @@ -29,6 +29,14 @@ public class MyDataClass public int Value1 { get; set; } public int Value2 { get; set; } public int Value3 { get; set; } + public decimal Value4 { get; set; } + + public DataSubclass SubValue { get; set; } + } + + public class DataSubclass + { + public decimal Value5 { get; set; } } public class DataIOWorkflow : IWorkflow @@ -47,18 +55,20 @@ public void Build(IWorkflowBuilder builder) public DataIOScenario() { - Setup(); + Setup(true); } [Fact] public void Scenario() { - var workflowId = StartWorkflow(new MyDataClass() { Value1 = 2, Value2 = 3 }); + decimal v4 = 1.235465673450897890m; + var workflowId = StartWorkflow(new MyDataClass() {Value1 = 2, Value2 = 3, Value4 = v4, SubValue = new DataSubclass() {Value5 = v4}}); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); UnhandledStepErrors.Count.Should().Be(0); GetData(workflowId).Value3.Should().Be(5); + GetData(workflowId).Value4.Should().Be(v4); } } } diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 32f9c1363..fd6508887 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -12,6 +12,7 @@ + diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/test/WorkflowCore.Testing/WorkflowTest.cs index f61d6be0a..32a1259be 100644 --- a/test/WorkflowCore.Testing/WorkflowTest.cs +++ b/test/WorkflowCore.Testing/WorkflowTest.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using MongoDB.Bson.Serialization; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -20,7 +21,7 @@ public abstract class WorkflowTest : IDisposable protected IPersistenceProvider PersistenceProvider; protected List UnhandledStepErrors = new List(); - protected virtual void Setup() + protected virtual void Setup(bool registerClassMap = false) { //setup dependency injection IServiceCollection services = new ServiceCollection(); @@ -33,6 +34,11 @@ protected virtual void Setup() var loggerFactory = serviceProvider.GetService(); //loggerFactory.AddConsole(LogLevel.Debug); + if (registerClassMap && !BsonClassMap.IsClassMapRegistered(typeof(TData))) + { + BsonClassMap.RegisterClassMap(map => map.AutoMap()); + } + PersistenceProvider = serviceProvider.GetService(); Host = serviceProvider.GetService(); Host.RegisterWorkflow(); From d0f233b9da59d7bc04a3f525f1c88da498c2aeae Mon Sep 17 00:00:00 2001 From: Mike Oldfield Date: Thu, 28 Jan 2021 15:53:53 +0200 Subject: [PATCH 285/462] Fixed build issue --- src/samples/WorkflowCore.TestSample01/NUnitTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/samples/WorkflowCore.TestSample01/NUnitTest.cs b/src/samples/WorkflowCore.TestSample01/NUnitTest.cs index 8c83661d8..cec26fe9e 100644 --- a/src/samples/WorkflowCore.TestSample01/NUnitTest.cs +++ b/src/samples/WorkflowCore.TestSample01/NUnitTest.cs @@ -13,9 +13,9 @@ namespace WorkflowCore.TestSample01 public class NUnitTest : WorkflowTest { [SetUp] - protected override void Setup() + protected override void Setup(bool registerClassMap = false) { - base.Setup(); + base.Setup(registerClassMap); } [Test] From 9a04d1c748cc53c2aa206c8e355badfdbf6fcec0 Mon Sep 17 00:00:00 2001 From: Mike Oldfield Date: Fri, 29 Jan 2021 17:27:13 +0200 Subject: [PATCH 286/462] Fixed generic lists serialization problem --- .../Services/DataObjectSerializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs index f9d34d004..2cf6cab3e 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs @@ -77,7 +77,7 @@ private string GetTypeNameFromXPath(object root, string xPath) if (subPath[0] == '[') { var index = Int32.Parse(subPath.Trim('[', ']')); - if (value.GetType().IsSubclassOf(typeof(IList)) || value.GetType().IsArray) + if ((value is IList) || value.GetType().IsArray) { IList list = (IList) value; value = list[index]; From b4fa2f6c5039613c702330005649476628cb9018 Mon Sep 17 00:00:00 2001 From: Mikhail Merkulov Date: Sat, 6 Feb 2021 21:11:09 +0200 Subject: [PATCH 287/462] Mongodb documentation updated with details on object serialization --- .../README.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/README.md b/src/providers/WorkflowCore.Persistence.MongoDB/README.md index 5dacb7c6c..911d8a9e4 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/README.md +++ b/src/providers/WorkflowCore.Persistence.MongoDB/README.md @@ -17,3 +17,35 @@ Use the .UseMongoDB extension method when building your service provider. ```C# services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); ``` + +### State object serialization + +By default (to maintain backwards compatibility), the state object is serialized using a two step serialization process using object -> JSON -> BSON serialization. +This approach has some limitations, for example you cannot control which types will be used in MongoDB for particular fields and you cannot use basic types that are not present in JSON (decimal, timestamp, etc). + +To eliminate these limitations, you can use a direct object -> BSON serialization and utilize all serialization possibilities that MongoDb driver provides. You can read more in the [MongoDb CSharp documentation](https://mongodb.github.io/mongo-csharp-driver/1.11/serialization/). +To enable direct serilization you need to register a class map for you state class somewhere in your startup process before you run `WorkflowHost`. + +```C# +private void RunWorkflow() +{ + var host = this._serviceProvider.GetService(); + if (host == null) + { + return; + } + + if (!BsonClassMap.IsClassMapRegistered(typeof(MyWorkflowState))) + { + BsonClassMap.RegisterClassMap(cm => + { + cm.AutoMap(); + }); + } + + host.RegisterWorkflow(); + + host.Start(); +} + +``` From d7ac340e6d0522ce3e42f89d3c8b23ee42d249b0 Mon Sep 17 00:00:00 2001 From: Mikhail Merkulov Date: Sat, 6 Feb 2021 21:11:23 +0200 Subject: [PATCH 288/462] Version bump --- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 2f465db0d..2ef5a4a1c 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.5 - 3.3.5.0 - 3.3.5.0 + 3.3.6 + 3.3.6.0 + 3.3.6.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.5 + 3.3.6 From 11368e8e0cbd4940648066d8016c573cb69ad9da Mon Sep 17 00:00:00 2001 From: Mikhail Merkulov Date: Tue, 9 Feb 2021 19:31:28 +0200 Subject: [PATCH 289/462] Rollback 3.3.6 -> 3.3.5 --- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 2ef5a4a1c..2f465db0d 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.6 - 3.3.6.0 - 3.3.6.0 + 3.3.5 + 3.3.5.0 + 3.3.5.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.6 + 3.3.5 From 96e5e66a4eac479749be4a53e8853426689e67d4 Mon Sep 17 00:00:00 2001 From: Mikhail Merkulov Date: Tue, 9 Feb 2021 19:32:21 +0200 Subject: [PATCH 290/462] Version bump --- .../WorkflowCore.Persistence.MongoDB.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index b6e6204c0..b365d33f6 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -14,11 +14,11 @@ false false false - 3.0.3 + 3.0.4 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 3.0.3.0 - 3.0.3.0 - 3.0.3 + 3.0.4.0 + 3.0.4.0 + 3.0.4 From cedc15176d0bd195980bc5f1c6dfe0ff4e001aca Mon Sep 17 00:00:00 2001 From: glucaci Date: Sun, 14 Feb 2021 11:48:00 +0100 Subject: [PATCH 291/462] Execute post middleware in any case --- src/WorkflowCore/Services/WorkflowExecutor.cs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index c6241fd64..0bdf8ce36 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -212,7 +212,10 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo workflow.NextExecution = null; if (workflow.Status == WorkflowStatus.Complete) + { + await RunPostMiddleware(workflow, def); return; + } foreach (var pointer in workflow.ExecutionPointers.Where(x => x.Active && (x.Children ?? new List()).Count == 0)) { @@ -242,18 +245,17 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo } if ((workflow.NextExecution != null) || (workflow.ExecutionPointers.Any(x => x.EndTime == null))) + { + await RunPostMiddleware(workflow, def); return; + } workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.UtcNow; - using (var scope = _serviceProvider.CreateScope()) - { - var middlewareRunner = scope.ServiceProvider.GetRequiredService(); - await middlewareRunner.RunPostMiddleware(workflow, def); - } + await RunPostMiddleware(workflow, def); - _publisher.PublishNotification(new WorkflowCompleted() + _publisher.PublishNotification(new WorkflowCompleted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, @@ -262,5 +264,14 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo Version = workflow.Version }); } + + private Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinition def) + { + using (var scope = _serviceProvider.CreateScope()) + { + var middlewareRunner = scope.ServiceProvider.GetRequiredService(); + return middlewareRunner.RunPostMiddleware(workflow, def); + } + } } } From f462d6e4f815f2e879c6cb78178f090a49baf4e9 Mon Sep 17 00:00:00 2001 From: glucaci Date: Sun, 14 Feb 2021 11:59:15 +0100 Subject: [PATCH 292/462] Add post middleware in more places --- src/WorkflowCore/Services/WorkflowExecutor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 0bdf8ce36..01ce4c924 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -222,6 +222,7 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo if (!pointer.SleepUntil.HasValue) { workflow.NextExecution = 0; + await RunPostMiddleware(workflow, def); return; } @@ -237,6 +238,7 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo if (!pointer.SleepUntil.HasValue) { workflow.NextExecution = 0; + await RunPostMiddleware(workflow, def); return; } From 55db92852cf1b3f8b1f20d24831ab911ef84cc3b Mon Sep 17 00:00:00 2001 From: glucaci Date: Sun, 14 Feb 2021 12:57:58 +0100 Subject: [PATCH 293/462] Fix workflow status mongo serialization --- .../Services/MongoPersistenceProvider.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 40d4e5409..041b9ae96 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Conventions; +using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -44,7 +45,8 @@ static MongoPersistenceProvider() x.MapProperty(y => y.WorkflowDefinitionId); x.MapProperty(y => y.Version); x.MapProperty(y => y.NextExecution); - x.MapProperty(y => y.Status); + x.MapProperty(y => y.Status) + .SetSerializer(new EnumSerializer(BsonType.String)); x.MapProperty(y => y.CreateTime); x.MapProperty(y => y.CompleteTime); x.MapProperty(y => y.ExecutionPointers); From e15b2a6afcfb587908e240975cf9120c513573dc Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 15 Feb 2021 08:00:05 +0100 Subject: [PATCH 294/462] Cleanup --- src/WorkflowCore/Services/WorkflowExecutor.cs | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 01ce4c924..ee58e32bb 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -96,7 +96,13 @@ public async Task Execute(WorkflowInstance workflow, Can _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); } ProcessAfterExecutionIteration(workflow, def, wfResult); - await DetermineNextExecutionTime(workflow, def); + DetermineNextExecutionTime(workflow, def); + + using (var scope = _serviceProvider.CreateScope()) + { + var middlewareRunner = scope.ServiceProvider.GetRequiredService(); + await middlewareRunner.RunPostMiddleware(workflow, def); + } return wfResult; } @@ -206,14 +212,13 @@ private void ProcessAfterExecutionIteration(WorkflowInstance workflow, WorkflowD } } - private async Task DetermineNextExecutionTime(WorkflowInstance workflow, WorkflowDefinition def) + private void DetermineNextExecutionTime(WorkflowInstance workflow, WorkflowDefinition def) { //TODO: move to own class workflow.NextExecution = null; if (workflow.Status == WorkflowStatus.Complete) { - await RunPostMiddleware(workflow, def); return; } @@ -222,7 +227,6 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo if (!pointer.SleepUntil.HasValue) { workflow.NextExecution = 0; - await RunPostMiddleware(workflow, def); return; } @@ -238,7 +242,6 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo if (!pointer.SleepUntil.HasValue) { workflow.NextExecution = 0; - await RunPostMiddleware(workflow, def); return; } @@ -248,15 +251,12 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo if ((workflow.NextExecution != null) || (workflow.ExecutionPointers.Any(x => x.EndTime == null))) { - await RunPostMiddleware(workflow, def); return; } workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.UtcNow; - await RunPostMiddleware(workflow, def); - _publisher.PublishNotification(new WorkflowCompleted { EventTimeUtc = _datetimeProvider.UtcNow, @@ -266,14 +266,5 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo Version = workflow.Version }); } - - private Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinition def) - { - using (var scope = _serviceProvider.CreateScope()) - { - var middlewareRunner = scope.ServiceProvider.GetRequiredService(); - return middlewareRunner.RunPostMiddleware(workflow, def); - } - } } } From 200f16fff9ab53fb1f3b5705eaaa1bef1ab44aa3 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 16 Feb 2021 08:14:45 -0800 Subject: [PATCH 295/462] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 20 ++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..471baed24 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,20 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From d0f3f90c23144bed4d0cb4b09008d7d36e8246f9 Mon Sep 17 00:00:00 2001 From: John Demetriou Date: Thu, 18 Feb 2021 11:14:59 +0200 Subject: [PATCH 296/462] Removed TODO. It's clear Item3 is definition --- src/WorkflowCore/Services/WorkflowRegistry.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/WorkflowCore/Services/WorkflowRegistry.cs b/src/WorkflowCore/Services/WorkflowRegistry.cs index c09189a1c..22f0ba608 100644 --- a/src/WorkflowCore/Services/WorkflowRegistry.cs +++ b/src/WorkflowCore/Services/WorkflowRegistry.cs @@ -23,7 +23,6 @@ public WorkflowDefinition GetDefinition(string workflowId, int? version = null) if (version.HasValue) { var entry = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version.Value); - // TODO: What in the heck does Item3 mean? return entry?.Item3; } else From 8361a0f5ed76ab52581b6fc36567d8e6bc522865 Mon Sep 17 00:00:00 2001 From: John Demetriou Date: Thu, 18 Feb 2021 11:15:35 +0200 Subject: [PATCH 297/462] Switched to named ValueTuples to improve code readability --- src/WorkflowCore/Services/WorkflowRegistry.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowRegistry.cs b/src/WorkflowCore/Services/WorkflowRegistry.cs index 22f0ba608..df4619621 100644 --- a/src/WorkflowCore/Services/WorkflowRegistry.cs +++ b/src/WorkflowCore/Services/WorkflowRegistry.cs @@ -11,7 +11,7 @@ namespace WorkflowCore.Services public class WorkflowRegistry : IWorkflowRegistry { private readonly IServiceProvider _serviceProvider; - private readonly BlockingCollection> _registry = new BlockingCollection>(); + private readonly BlockingCollection<(string, int, WorkflowDefinition)> _registry = new BlockingCollection<(string, int, WorkflowDefinition)>(); public WorkflowRegistry(IServiceProvider serviceProvider) { @@ -20,23 +20,24 @@ public WorkflowRegistry(IServiceProvider serviceProvider) public WorkflowDefinition GetDefinition(string workflowId, int? version = null) { + (string workflowId, int version, WorkflowDefinition definition) workflowEntry; if (version.HasValue) { - var entry = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version.Value); - return entry?.Item3; + workflowEntry = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version.Value); } else { - var entry = _registry.Where(x => x.Item1 == workflowId).OrderByDescending(x => x.Item2) - .FirstOrDefault(); - return entry?.Item3; + workflowEntry = _registry.Where(x => x.Item1 == workflowId).OrderByDescending(x => x.Item2) + .FirstOrDefault(); } + + return workflowEntry != default ? workflowEntry.definition : default; } public void DeregisterWorkflow(string workflowId, int version) { var definition = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version); - if (definition != null) + if (definition != default) { _registry.TryTake(out definition); } @@ -49,10 +50,10 @@ public void RegisterWorkflow(IWorkflow workflow) throw new InvalidOperationException($"Workflow {workflow.Id} version {workflow.Version} is already registered"); } - var builder = _serviceProvider.GetService().UseData(); + var builder = _serviceProvider.GetService().UseData(); workflow.Build(builder); var def = builder.Build(workflow.Id, workflow.Version); - _registry.Add(Tuple.Create(workflow.Id, workflow.Version, def)); + _registry.Add((workflow.Id, workflow.Version, def)); } public void RegisterWorkflow(WorkflowDefinition definition) @@ -62,7 +63,7 @@ public void RegisterWorkflow(WorkflowDefinition definition) throw new InvalidOperationException($"Workflow {definition.Id} version {definition.Version} is already registered"); } - _registry.Add(Tuple.Create(definition.Id, definition.Version, definition)); + _registry.Add((definition.Id, definition.Version, definition)); } public void RegisterWorkflow(IWorkflow workflow) @@ -76,13 +77,13 @@ public void RegisterWorkflow(IWorkflow workflow) var builder = _serviceProvider.GetService().UseData(); workflow.Build(builder); var def = builder.Build(workflow.Id, workflow.Version); - _registry.Add(Tuple.Create(workflow.Id, workflow.Version, def)); + _registry.Add((workflow.Id, workflow.Version, def)); } public bool IsRegistered(string workflowId, int version) { var definition = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version); - return (definition != null); + return definition != default; } public IEnumerable GetAllDefinitions() @@ -90,4 +91,4 @@ public IEnumerable GetAllDefinitions() return _registry.Select(i => i.Item3); } } -} +} \ No newline at end of file From 653fa003080ffebeb33be150e5d7524904265fc5 Mon Sep 17 00:00:00 2001 From: john Date: Sun, 21 Feb 2021 10:10:48 +0200 Subject: [PATCH 298/462] Switched to named tuple --- src/WorkflowCore/Services/WorkflowRegistry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WorkflowCore/Services/WorkflowRegistry.cs b/src/WorkflowCore/Services/WorkflowRegistry.cs index df4619621..66a310ec0 100644 --- a/src/WorkflowCore/Services/WorkflowRegistry.cs +++ b/src/WorkflowCore/Services/WorkflowRegistry.cs @@ -11,7 +11,7 @@ namespace WorkflowCore.Services public class WorkflowRegistry : IWorkflowRegistry { private readonly IServiceProvider _serviceProvider; - private readonly BlockingCollection<(string, int, WorkflowDefinition)> _registry = new BlockingCollection<(string, int, WorkflowDefinition)>(); + private readonly BlockingCollection<(string workfloId, int version, WorkflowDefinition definition)> _registry = new BlockingCollection<(string, int, WorkflowDefinition)>(); public WorkflowRegistry(IServiceProvider serviceProvider) { From 141f70407bf8d5eaeeec7d4081b5afaf4afb6d97 Mon Sep 17 00:00:00 2001 From: DevsAnon Date: Tue, 23 Feb 2021 09:17:05 +0200 Subject: [PATCH 299/462] Use ValueTuple named values everywhere in WorkflowRegistry --- src/WorkflowCore/Services/WorkflowRegistry.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowRegistry.cs b/src/WorkflowCore/Services/WorkflowRegistry.cs index 66a310ec0..01d0625a5 100644 --- a/src/WorkflowCore/Services/WorkflowRegistry.cs +++ b/src/WorkflowCore/Services/WorkflowRegistry.cs @@ -11,7 +11,7 @@ namespace WorkflowCore.Services public class WorkflowRegistry : IWorkflowRegistry { private readonly IServiceProvider _serviceProvider; - private readonly BlockingCollection<(string workfloId, int version, WorkflowDefinition definition)> _registry = new BlockingCollection<(string, int, WorkflowDefinition)>(); + private readonly BlockingCollection<(string workflowId, int version, WorkflowDefinition definition)> _registry = new BlockingCollection<(string, int, WorkflowDefinition)>(); public WorkflowRegistry(IServiceProvider serviceProvider) { @@ -23,11 +23,11 @@ public WorkflowDefinition GetDefinition(string workflowId, int? version = null) (string workflowId, int version, WorkflowDefinition definition) workflowEntry; if (version.HasValue) { - workflowEntry = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version.Value); + workflowEntry = _registry.FirstOrDefault(x => x.workflowId == workflowId && x.version == version.Value); } else { - workflowEntry = _registry.Where(x => x.Item1 == workflowId).OrderByDescending(x => x.Item2) + workflowEntry = _registry.Where(x => x.workflowId == workflowId).OrderByDescending(x => x.version) .FirstOrDefault(); } @@ -36,7 +36,7 @@ public WorkflowDefinition GetDefinition(string workflowId, int? version = null) public void DeregisterWorkflow(string workflowId, int version) { - var definition = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version); + var definition = _registry.FirstOrDefault(x => x.workflowId == workflowId && x.version == version); if (definition != default) { _registry.TryTake(out definition); @@ -45,7 +45,7 @@ public void DeregisterWorkflow(string workflowId, int version) public void RegisterWorkflow(IWorkflow workflow) { - if (_registry.Any(x => x.Item1 == workflow.Id && x.Item2 == workflow.Version)) + if (_registry.Any(x => x.workflowId == workflow.Id && x.version == workflow.Version)) { throw new InvalidOperationException($"Workflow {workflow.Id} version {workflow.Version} is already registered"); } @@ -58,7 +58,7 @@ public void RegisterWorkflow(IWorkflow workflow) public void RegisterWorkflow(WorkflowDefinition definition) { - if (_registry.Any(x => x.Item1 == definition.Id && x.Item2 == definition.Version)) + if (_registry.Any(x => x.workflowId == definition.Id && x.version == definition.Version)) { throw new InvalidOperationException($"Workflow {definition.Id} version {definition.Version} is already registered"); } @@ -69,7 +69,7 @@ public void RegisterWorkflow(WorkflowDefinition definition) public void RegisterWorkflow(IWorkflow workflow) where TData : new() { - if (_registry.Any(x => x.Item1 == workflow.Id && x.Item2 == workflow.Version)) + if (_registry.Any(x => x.workflowId == workflow.Id && x.version == workflow.Version)) { throw new InvalidOperationException($"Workflow {workflow.Id} version {workflow.Version} is already registered"); } @@ -82,13 +82,13 @@ public void RegisterWorkflow(IWorkflow workflow) public bool IsRegistered(string workflowId, int version) { - var definition = _registry.FirstOrDefault(x => x.Item1 == workflowId && x.Item2 == version); + var definition = _registry.FirstOrDefault(x => x.workflowId == workflowId && x.version == version); return definition != default; } public IEnumerable GetAllDefinitions() { - return _registry.Select(i => i.Item3); + return _registry.Select(i => i.definition); } } } \ No newline at end of file From dbc5af37797e2d38f23d3b91a7c3f32eefdd4915 Mon Sep 17 00:00:00 2001 From: rapmue Date: Wed, 24 Feb 2021 09:28:53 +0100 Subject: [PATCH 300/462] Fixed wrong JSON and YAML definitions It was unclear in the JSON and YAML definitions what the related properties of the step and the DTO are. Hopefully I understood it correctly. --- docs/getting-started.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 49ac1f38e..26bc01879 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -142,7 +142,7 @@ public class MyDataClass { public int Value1 { get; set; } public int Value2 { get; set; } - public int Value3 { get; set; } + public int Answer { get; set; } } //Our workflow definition with strongly typed internal data and mapped inputs & outputs @@ -154,9 +154,9 @@ public class PassingDataWorkflow : IWorkflow .StartWith() .Input(step => step.Input1, data => data.Value1) .Input(step => step.Input2, data => data.Value2) - .Output(data => data.Value3, step => step.Output) + .Output(data => data.Answer, step => step.Output) .Then() - .Input(step => step.Message, data => "The answer is " + data.Value3.ToString()); + .Input(step => step.Message, data => "The answer is " + data.Answer.ToString()); } ... } @@ -175,8 +175,8 @@ or in jSON format "StepType": "MyApp.AddNumbers, MyApp", "NextStepId": "ShowResult", "Inputs": { - "Value1": "data.Value1", - "Value2": "data.Value2" + "Input1": "data.Value1", + "Input2": "data.Value2" }, "Outputs": { "Answer": "step.Output" @@ -186,7 +186,7 @@ or in jSON format "Id": "ShowResult", "StepType": "MyApp.CustomMessage, MyApp", "Inputs": { - "Message": "\"The answer is \" + data.Value1" + "Message": "\"The answer is \" + data.Answer" } } ] @@ -203,14 +203,14 @@ Steps: StepType: MyApp.AddNumbers, MyApp NextStepId: ShowResult Inputs: - Value1: data.Value1 - Value2: data.Value2 + Input1: data.Value1 + Input2: data.Value2 Outputs: Answer: step.Output - Id: ShowResult StepType: MyApp.CustomMessage, MyApp Inputs: - Message: '"The answer is " + data.Value1' + Message: '"The answer is " + data.Answer' ``` From 3ca050f2cb0d6603b374e63e700313ed0711917e Mon Sep 17 00:00:00 2001 From: macrian Date: Thu, 11 Mar 2021 10:34:50 +0200 Subject: [PATCH 301/462] Remove unnecessary parenthesis from everywhere --- .../Services/DefinitionLoader.cs | 2 +- src/WorkflowCore/Models/ExecutionResult.cs | 14 ++-- .../Models/Search/SearchFilter.cs | 30 ++++---- src/WorkflowCore/Models/StepBody.cs | 6 +- src/WorkflowCore/Primitives/Foreach.cs | 4 +- src/WorkflowCore/Primitives/If.cs | 2 +- src/WorkflowCore/Primitives/OutcomeSwitch.cs | 2 +- src/WorkflowCore/Primitives/Recur.cs | 4 +- src/WorkflowCore/Primitives/Schedule.cs | 4 +- src/WorkflowCore/Primitives/Sequence.cs | 2 +- src/WorkflowCore/Primitives/When.cs | 2 +- src/WorkflowCore/Primitives/While.cs | 2 +- .../Services/ActivityController.cs | 8 +-- .../Services/BackgroundTasks/EventConsumer.cs | 2 +- .../SingleNodeQueueProvider.cs | 2 +- .../Services/ErrorHandlers/SuspendHandler.cs | 2 +- .../ErrorHandlers/TerminateHandler.cs | 2 +- .../Services/ExecutionPointerFactory.cs | 6 +- .../Services/ExecutionResultProcessor.cs | 6 +- .../Services/FluentBuilders/StepBuilder.cs | 40 +++++------ .../Services/WorkflowController.cs | 8 +-- src/WorkflowCore/Services/WorkflowExecutor.cs | 12 ++-- .../Models/UserStepContainer.cs | 2 +- .../WorkflowCore.Users/Primitives/UserTask.cs | 4 +- .../StepBuilderExtensions.cs | 4 +- .../WorkflowHostExtensions.cs | 2 +- .../WorkflowInstanceExtensions.cs | 2 +- .../Services/UserTaskBuilder.cs | 2 +- .../ExtensionMethods.cs | 2 +- .../Services/DataObjectSerializer.cs | 2 +- .../ModelExtensions.cs | 28 ++++---- .../Services/DynamoDbProvisioner.cs | 56 +++++++-------- .../Services/DynamoLockProvider.cs | 14 ++-- .../Services/DynamoPersistenceProvider.cs | 70 +++++++++---------- .../Services/KinesisProvider.cs | 8 +-- .../Services/KinesisStreamConsumer.cs | 12 ++-- .../Services/KinesisTracker.cs | 14 ++-- .../Models/PersistedEvent.cs | 6 +- .../Models/PersistedSubscription.cs | 6 +- .../Models/PersistedWorkflow.cs | 6 +- .../Services/CosmosDbProvisioner.cs | 4 +- .../Models/WorkflowSearchModel.cs | 8 +-- .../Services/RedisLifeCycleEventHub.cs | 2 +- .../Services/RedisLockProvider.cs | 2 +- .../Services/RedisPersistenceProvider.cs | 2 +- .../Services/RedisQueueProvider.cs | 2 +- .../Services/RabbitMQProvider.cs | 2 +- .../ServiceCollectionExtensions.cs | 2 +- .../Services/QueueConfigProvider.cs | 2 +- .../WorkflowCore.Sample09/ForEachWorkflow.cs | 2 +- .../ForEachSyncWorkflow.cs | 2 +- src/samples/WorkflowCore.Sample10/Program.cs | 2 +- src/samples/WorkflowCore.Sample11/Program.cs | 2 +- src/samples/WorkflowCore.Sample12/Program.cs | 2 +- src/samples/WorkflowCore.Sample18/Program.cs | 2 +- .../WorkflowCore.TestSample01/NUnitTest.cs | 2 +- .../WorkflowCore.TestSample01/xUnitTest.cs | 2 +- test/Docker.Testify/DockerSetup.cs | 6 +- test/ScratchPad/ElasticTest.cs | 4 +- test/ScratchPad/Program.cs | 2 +- .../Scenarios/ActivityScenario.cs | 4 +- .../Scenarios/CompensationScenario.cs | 4 +- .../Scenarios/CompensationScenario2.cs | 4 +- .../Scenarios/DataIOScenario.cs | 2 +- .../Scenarios/DecisionScenario.cs | 4 +- .../Scenarios/DynamicDataIOScenario.cs | 2 +- .../Scenarios/EventScenario.cs | 2 +- .../Scenarios/ForeachScenario.cs | 2 +- .../Scenarios/ForeachSyncScenario.cs | 2 +- .../Scenarios/IfScenario.cs | 2 +- .../Scenarios/SagaScenario.cs | 4 +- .../Scenarios/StoredJsonScenario.cs | 4 +- .../Scenarios/StoredYamlScenario.cs | 2 +- .../Scenarios/WhenScenario.cs | 2 +- .../Scenarios/WhileScenario.cs | 2 +- .../SearchIndexTests.cs | 16 ++--- test/WorkflowCore.TestAssets/Utils.cs | 2 +- test/WorkflowCore.Testing/JsonWorkflowTest.cs | 2 +- test/WorkflowCore.Testing/WorkflowTest.cs | 2 +- test/WorkflowCore.Testing/YamlWorkflowTest.cs | 2 +- .../BasePersistenceFixture.cs | 54 +++++++------- .../Models/MemberMapParameterTests.cs | 8 +-- .../ExecutionResultProcessorFixture.cs | 36 +++++----- .../Services/WorkflowExecutorFixture.cs | 44 ++++++------ 84 files changed, 332 insertions(+), 332 deletions(-) diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index db92d5430..d408bfc07 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -250,7 +250,7 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo private void AttachOutcomes(StepSourceV1 source, Type dataType, WorkflowStep step) { if (!string.IsNullOrEmpty(source.NextStepId)) - step.Outcomes.Add(new ValueOutcome() { ExternalNextStepId = $"{source.NextStepId}" }); + step.Outcomes.Add(new ValueOutcome { ExternalNextStepId = $"{source.NextStepId}" }); var dataParameter = Expression.Parameter(dataType, "data"); var outcomeParameter = Expression.Parameter(typeof(object), "outcome"); diff --git a/src/WorkflowCore/Models/ExecutionResult.cs b/src/WorkflowCore/Models/ExecutionResult.cs index 54c2c55f2..14c562802 100644 --- a/src/WorkflowCore/Models/ExecutionResult.cs +++ b/src/WorkflowCore/Models/ExecutionResult.cs @@ -35,7 +35,7 @@ public ExecutionResult(object outcome) public static ExecutionResult Outcome(object value) { - return new ExecutionResult() + return new ExecutionResult { Proceed = true, OutcomeValue = value @@ -44,7 +44,7 @@ public static ExecutionResult Outcome(object value) public static ExecutionResult Next() { - return new ExecutionResult() + return new ExecutionResult { Proceed = true, OutcomeValue = null @@ -53,7 +53,7 @@ public static ExecutionResult Next() public static ExecutionResult Persist(object persistenceData) { - return new ExecutionResult() + return new ExecutionResult { Proceed = false, PersistenceData = persistenceData @@ -62,7 +62,7 @@ public static ExecutionResult Persist(object persistenceData) public static ExecutionResult Branch(List branches, object persistenceData) { - return new ExecutionResult() + return new ExecutionResult { Proceed = false, PersistenceData = persistenceData, @@ -72,7 +72,7 @@ public static ExecutionResult Branch(List branches, object persistenceDa public static ExecutionResult Sleep(TimeSpan duration, object persistenceData) { - return new ExecutionResult() + return new ExecutionResult { Proceed = false, SleepFor = duration, @@ -82,7 +82,7 @@ public static ExecutionResult Sleep(TimeSpan duration, object persistenceData) public static ExecutionResult WaitForEvent(string eventName, string eventKey, DateTime effectiveDate) { - return new ExecutionResult() + return new ExecutionResult { Proceed = false, EventName = eventName, @@ -93,7 +93,7 @@ public static ExecutionResult WaitForEvent(string eventName, string eventKey, Da public static ExecutionResult WaitForActivity(string activityName, object subscriptionData, DateTime effectiveDate) { - return new ExecutionResult() + return new ExecutionResult { Proceed = false, EventName = Event.EventTypeActivity, diff --git a/src/WorkflowCore/Models/Search/SearchFilter.cs b/src/WorkflowCore/Models/Search/SearchFilter.cs index fd7d8c7a4..b4d7ff66b 100644 --- a/src/WorkflowCore/Models/Search/SearchFilter.cs +++ b/src/WorkflowCore/Models/Search/SearchFilter.cs @@ -21,13 +21,13 @@ public class ScalarFilter : SearchFilter { public object Value { get; set; } - public static SearchFilter Equals(Expression> property, object value) => new ScalarFilter() + public static SearchFilter Equals(Expression> property, object value) => new ScalarFilter { Property = property, Value = value }; - public static SearchFilter Equals(Expression> property, object value) => new ScalarFilter() + public static SearchFilter Equals(Expression> property, object value) => new ScalarFilter { IsData = true, DataType = typeof(T), @@ -41,26 +41,26 @@ public class DateRangeFilter : SearchFilter public DateTime? BeforeValue { get; set; } public DateTime? AfterValue { get; set; } - public static DateRangeFilter Before(Expression> property, DateTime value) => new DateRangeFilter() + public static DateRangeFilter Before(Expression> property, DateTime value) => new DateRangeFilter { Property = property, BeforeValue = value }; - public static DateRangeFilter After(Expression> property, DateTime value) => new DateRangeFilter() + public static DateRangeFilter After(Expression> property, DateTime value) => new DateRangeFilter { Property = property, AfterValue = value }; - public static DateRangeFilter Between(Expression> property, DateTime start, DateTime end) => new DateRangeFilter() + public static DateRangeFilter Between(Expression> property, DateTime start, DateTime end) => new DateRangeFilter { Property = property, BeforeValue = end, AfterValue = start }; - public static DateRangeFilter Before(Expression> property, DateTime value) => new DateRangeFilter() + public static DateRangeFilter Before(Expression> property, DateTime value) => new DateRangeFilter { IsData = true, DataType = typeof(T), @@ -68,7 +68,7 @@ public class DateRangeFilter : SearchFilter BeforeValue = value }; - public static DateRangeFilter After(Expression> property, DateTime value) => new DateRangeFilter() + public static DateRangeFilter After(Expression> property, DateTime value) => new DateRangeFilter { IsData = true, DataType = typeof(T), @@ -76,7 +76,7 @@ public class DateRangeFilter : SearchFilter AfterValue = value }; - public static DateRangeFilter Between(Expression> property, DateTime start, DateTime end) => new DateRangeFilter() + public static DateRangeFilter Between(Expression> property, DateTime start, DateTime end) => new DateRangeFilter { IsData = true, DataType = typeof(T), @@ -91,26 +91,26 @@ public class NumericRangeFilter : SearchFilter public double? LessValue { get; set; } public double? GreaterValue { get; set; } - public static NumericRangeFilter LessThan(Expression> property, double value) => new NumericRangeFilter() + public static NumericRangeFilter LessThan(Expression> property, double value) => new NumericRangeFilter { Property = property, LessValue = value }; - public static NumericRangeFilter GreaterThan(Expression> property, double value) => new NumericRangeFilter() + public static NumericRangeFilter GreaterThan(Expression> property, double value) => new NumericRangeFilter { Property = property, GreaterValue = value }; - public static NumericRangeFilter Between(Expression> property, double start, double end) => new NumericRangeFilter() + public static NumericRangeFilter Between(Expression> property, double start, double end) => new NumericRangeFilter { Property = property, LessValue = end, GreaterValue = start }; - public static NumericRangeFilter LessThan(Expression> property, double value) => new NumericRangeFilter() + public static NumericRangeFilter LessThan(Expression> property, double value) => new NumericRangeFilter { IsData = true, DataType = typeof(T), @@ -118,7 +118,7 @@ public class NumericRangeFilter : SearchFilter LessValue = value }; - public static NumericRangeFilter GreaterThan(Expression> property, double value) => new NumericRangeFilter() + public static NumericRangeFilter GreaterThan(Expression> property, double value) => new NumericRangeFilter { IsData = true, DataType = typeof(T), @@ -126,7 +126,7 @@ public class NumericRangeFilter : SearchFilter GreaterValue = value }; - public static NumericRangeFilter Between(Expression> property, double start, double end) => new NumericRangeFilter() + public static NumericRangeFilter Between(Expression> property, double start, double end) => new NumericRangeFilter { IsData = true, DataType = typeof(T), @@ -144,7 +144,7 @@ protected StatusFilter() Property = lambda; } - public static StatusFilter Equals(WorkflowStatus value) => new StatusFilter() + public static StatusFilter Equals(WorkflowStatus value) => new StatusFilter { Value = value.ToString() }; diff --git a/src/WorkflowCore/Models/StepBody.cs b/src/WorkflowCore/Models/StepBody.cs index 5ec927528..bb495c7a5 100644 --- a/src/WorkflowCore/Models/StepBody.cs +++ b/src/WorkflowCore/Models/StepBody.cs @@ -15,7 +15,7 @@ public Task RunAsync(IStepExecutionContext context) protected ExecutionResult OutcomeResult(object value) { - return new ExecutionResult() + return new ExecutionResult { Proceed = true, OutcomeValue = value @@ -24,7 +24,7 @@ protected ExecutionResult OutcomeResult(object value) protected ExecutionResult PersistResult(object persistenceData) { - return new ExecutionResult() + return new ExecutionResult { Proceed = false, PersistenceData = persistenceData @@ -33,7 +33,7 @@ protected ExecutionResult PersistResult(object persistenceData) protected ExecutionResult SleepResult(object persistenceData, TimeSpan sleep) { - return new ExecutionResult() + return new ExecutionResult { Proceed = false, PersistenceData = persistenceData, diff --git a/src/WorkflowCore/Primitives/Foreach.cs b/src/WorkflowCore/Primitives/Foreach.cs index d72371e56..1a64ddfdb 100644 --- a/src/WorkflowCore/Primitives/Foreach.cs +++ b/src/WorkflowCore/Primitives/Foreach.cs @@ -18,11 +18,11 @@ public override ExecutionResult Run(IStepExecutionContext context) var values = Collection.Cast(); if (RunParallel) { - return ExecutionResult.Branch(new List(values), new IteratorPersistenceData() { ChildrenActive = true }); + return ExecutionResult.Branch(new List(values), new IteratorPersistenceData { ChildrenActive = true }); } else { - return ExecutionResult.Branch(new List(new object[] { values.ElementAt(0) }), new IteratorPersistenceData() { ChildrenActive = true }); + return ExecutionResult.Branch(new List(new object[] { values.ElementAt(0) }), new IteratorPersistenceData { ChildrenActive = true }); } } diff --git a/src/WorkflowCore/Primitives/If.cs b/src/WorkflowCore/Primitives/If.cs index 1f7085788..b3950bf7c 100644 --- a/src/WorkflowCore/Primitives/If.cs +++ b/src/WorkflowCore/Primitives/If.cs @@ -15,7 +15,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (Condition) { - return ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); + return ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); } return ExecutionResult.Next(); diff --git a/src/WorkflowCore/Primitives/OutcomeSwitch.cs b/src/WorkflowCore/Primitives/OutcomeSwitch.cs index d2dcc1e79..53cdc4729 100644 --- a/src/WorkflowCore/Primitives/OutcomeSwitch.cs +++ b/src/WorkflowCore/Primitives/OutcomeSwitch.cs @@ -12,7 +12,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (context.PersistenceData == null) { - var result = ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); + var result = ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); result.OutcomeValue = GetPreviousOutcome(context); return result; } diff --git a/src/WorkflowCore/Primitives/Recur.cs b/src/WorkflowCore/Primitives/Recur.cs index ccc41e7d0..4b6189fb5 100644 --- a/src/WorkflowCore/Primitives/Recur.cs +++ b/src/WorkflowCore/Primitives/Recur.cs @@ -18,10 +18,10 @@ public override ExecutionResult Run(IStepExecutionContext context) return ExecutionResult.Next(); } - return new ExecutionResult() + return new ExecutionResult { Proceed = false, - BranchValues = new List() { null }, + BranchValues = new List { null }, SleepFor = Interval }; } diff --git a/src/WorkflowCore/Primitives/Schedule.cs b/src/WorkflowCore/Primitives/Schedule.cs index e279cb232..7eb63c8c1 100644 --- a/src/WorkflowCore/Primitives/Schedule.cs +++ b/src/WorkflowCore/Primitives/Schedule.cs @@ -13,14 +13,14 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (context.PersistenceData == null) { - return ExecutionResult.Sleep(Interval, new SchedulePersistenceData() { Elapsed = false }); + return ExecutionResult.Sleep(Interval, new SchedulePersistenceData { Elapsed = false }); } if (context.PersistenceData is SchedulePersistenceData) { if (!((SchedulePersistenceData)context.PersistenceData).Elapsed) { - return ExecutionResult.Branch(new List() { context.Item }, new SchedulePersistenceData() { Elapsed = true }); + return ExecutionResult.Branch(new List { context.Item }, new SchedulePersistenceData { Elapsed = true }); } if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) diff --git a/src/WorkflowCore/Primitives/Sequence.cs b/src/WorkflowCore/Primitives/Sequence.cs index d31206a79..d3e0b827d 100644 --- a/src/WorkflowCore/Primitives/Sequence.cs +++ b/src/WorkflowCore/Primitives/Sequence.cs @@ -11,7 +11,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (context.PersistenceData == null) { - return ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); + return ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); } if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) diff --git a/src/WorkflowCore/Primitives/When.cs b/src/WorkflowCore/Primitives/When.cs index f5f292ad1..a2ab32252 100644 --- a/src/WorkflowCore/Primitives/When.cs +++ b/src/WorkflowCore/Primitives/When.cs @@ -25,7 +25,7 @@ public override ExecutionResult Run(IStepExecutionContext context) if (context.PersistenceData == null) { - return ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); + return ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); } if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) diff --git a/src/WorkflowCore/Primitives/While.cs b/src/WorkflowCore/Primitives/While.cs index ba5abc2e6..7716ed0b0 100644 --- a/src/WorkflowCore/Primitives/While.cs +++ b/src/WorkflowCore/Primitives/While.cs @@ -15,7 +15,7 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (Condition) { - return ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); + return ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); } return ExecutionResult.Next(); diff --git a/src/WorkflowCore/Services/ActivityController.cs b/src/WorkflowCore/Services/ActivityController.cs index f39bb64b2..04af6ef9f 100644 --- a/src/WorkflowCore/Services/ActivityController.cs +++ b/src/WorkflowCore/Services/ActivityController.cs @@ -47,7 +47,7 @@ public async Task GetPendingActivity(string activityName, strin try { var token = Token.Create(subscription.Id, subscription.EventKey); - var result = new PendingActivity() + var result = new PendingActivity { Token = token.Encode(), ActivityName = subscription.EventKey, @@ -75,7 +75,7 @@ public async Task ReleaseActivityToken(string token) public async Task SubmitActivitySuccess(string token, object result) { - await SubmitActivityResult(token, new ActivityResult() + await SubmitActivityResult(token, new ActivityResult { Data = result, Status = ActivityResult.StatusType.Success @@ -84,7 +84,7 @@ public async Task SubmitActivitySuccess(string token, object result) public async Task SubmitActivityFailure(string token, object result) { - await SubmitActivityResult(token, new ActivityResult() + await SubmitActivityResult(token, new ActivityResult { Data = result, Status = ActivityResult.StatusType.Fail @@ -120,7 +120,7 @@ public string Encode() public static Token Create(string subscriptionId, string activityName) { - return new Token() + return new Token { SubscriptionId = subscriptionId, ActivityName = activityName, diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index bdd730fca..9a5c825c4 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -60,7 +60,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance await _eventRepository.MarkEventProcessed(itemId); return; } - subs = new List() { activity }; + subs = new List { activity }; } else { diff --git a/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs b/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs index 8e0bd8dda..56f63b550 100644 --- a/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/SingleNodeQueueProvider.cs @@ -14,7 +14,7 @@ namespace WorkflowCore.Services public class SingleNodeQueueProvider : IQueueProvider { - private readonly Dictionary> _queues = new Dictionary>() + private readonly Dictionary> _queues = new Dictionary> { [QueueType.Workflow] = new BlockingCollection(), [QueueType.Event] = new BlockingCollection(), diff --git a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs index 1cf6f7941..7c5fad05b 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs @@ -22,7 +22,7 @@ public SuspendHandler(ILifeCycleEventPublisher eventPublisher, IDateTimeProvider public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue bubbleUpQueue) { workflow.Status = WorkflowStatus.Suspended; - _eventPublisher.PublishNotification(new WorkflowSuspended() + _eventPublisher.PublishNotification(new WorkflowSuspended { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, diff --git a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs index d89f7f470..89fb05eae 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs @@ -22,7 +22,7 @@ public TerminateHandler(ILifeCycleEventPublisher eventPublisher, IDateTimeProvid public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue bubbleUpQueue) { workflow.Status = WorkflowStatus.Terminated; - _eventPublisher.PublishNotification(new WorkflowTerminated() + _eventPublisher.PublishNotification(new WorkflowTerminated { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, diff --git a/src/WorkflowCore/Services/ExecutionPointerFactory.cs b/src/WorkflowCore/Services/ExecutionPointerFactory.cs index 65f5a2f8f..937e83d99 100644 --- a/src/WorkflowCore/Services/ExecutionPointerFactory.cs +++ b/src/WorkflowCore/Services/ExecutionPointerFactory.cs @@ -24,7 +24,7 @@ public ExecutionPointer BuildGenesisPointer(WorkflowDefinition def) public ExecutionPointer BuildNextPointer(WorkflowDefinition def, ExecutionPointer pointer, IStepOutcome outcomeTarget) { var nextId = GenerateId(); - return new ExecutionPointer() + return new ExecutionPointer { Id = nextId, PredecessorId = pointer.Id, @@ -44,7 +44,7 @@ public ExecutionPointer BuildChildPointer(WorkflowDefinition def, ExecutionPoint childScope.Insert(0, pointer.Id); pointer.Children.Add(childPointerId); - return new ExecutionPointer() + return new ExecutionPointer { Id = childPointerId, PredecessorId = pointer.Id, @@ -60,7 +60,7 @@ public ExecutionPointer BuildChildPointer(WorkflowDefinition def, ExecutionPoint public ExecutionPointer BuildCompensationPointer(WorkflowDefinition def, ExecutionPointer pointer, ExecutionPointer exceptionPointer, int compensationStepId) { var nextId = GenerateId(); - return new ExecutionPointer() + return new ExecutionPointer { Id = nextId, PredecessorId = exceptionPointer.Id, diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index 781f3d39d..3c684945f 100755 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -44,7 +44,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition pointer.Active = false; pointer.Status = PointerStatus.WaitingForEvent; - workflowResult.Subscriptions.Add(new EventSubscription() + workflowResult.Subscriptions.Add(new EventSubscription { WorkflowId = workflow.Id, StepId = pointer.StepId, @@ -77,7 +77,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition subsequent.Active = true; } - _eventPublisher.PublishNotification(new StepCompleted() + _eventPublisher.PublishNotification(new StepCompleted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, @@ -102,7 +102,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception) { - _eventPublisher.PublishNotification(new WorkflowError() + _eventPublisher.PublishNotification(new WorkflowError { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index e1d4dcc76..f5d25730a 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -45,7 +45,7 @@ public IStepBuilder Then(Action> } newStep.Name = newStep.Name ?? typeof(TStep).Name; - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -53,7 +53,7 @@ public IStepBuilder Then(Action> public IStepBuilder Then(IStepBuilder newStep) where TStep : IStepBody { - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Step.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Step.Id }); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep.Step); return stepBuilder; } @@ -64,7 +64,7 @@ public IStepBuilder Then(Func(WorkflowBuilder, newStep); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -74,13 +74,13 @@ public IStepBuilder Then(Action bo WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); stepBuilder.Input(x => x.Body, x => body); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } public IStepBuilder Attach(string id) { - Step.Outcomes.Add(new ValueOutcome() + Step.Outcomes.Add(new ValueOutcome { ExternalNextStepId = id }); @@ -184,7 +184,7 @@ public IStepBuilder WaitFor(string eventName, Expression step.EffectiveDate, effectiveDate); } - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -203,7 +203,7 @@ public IStepBuilder WaitFor(string eventName, Expression step.EffectiveDate, effectiveDate); } - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -259,7 +259,7 @@ public IStepBuilder EndWorkflow() { EndStep newStep = new EndStep(); WorkflowBuilder.AddStep(newStep); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return this; } @@ -272,7 +272,7 @@ public IStepBuilder Delay(Expression> period WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -286,7 +286,7 @@ public IStepBuilder Decide(Expression> expres WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -301,7 +301,7 @@ public IContainerStepBuilder ForEach(Expression(WorkflowBuilder, newStep); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -319,7 +319,7 @@ public IContainerStepBuilder ForEach(Expression(WorkflowBuilder, newStep); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -335,7 +335,7 @@ public IContainerStepBuilder While(Expression(WorkflowBuilder, newStep); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -350,7 +350,7 @@ public IContainerStepBuilder If(Expression> con WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -367,7 +367,7 @@ public IContainerStepBuilder When(Expression(); WorkflowBuilder.AddStep(switchStep); - Step.Outcomes.Add(new ValueOutcome() + Step.Outcomes.Add(new ValueOutcome { NextStep = switchStep.Id, Label = label @@ -391,7 +391,7 @@ public IStepBuilder Saga(Action> builde var newStep = new SagaContainer(); WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); builder.Invoke(WorkflowBuilder); stepBuilder.Step.Children.Add(stepBuilder.Step.Id + 1); //TODO: make more elegant @@ -405,7 +405,7 @@ public IParallelStepBuilder Parallel() WorkflowBuilder.AddStep(newStep); var stepBuilder = new ParallelStepBuilder(WorkflowBuilder, newBuilder, newBuilder); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -418,7 +418,7 @@ public IContainerStepBuilder Schedule(Expression(WorkflowBuilder, newStep, this); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -435,7 +435,7 @@ public IContainerStepBuilder Recur(Expression(WorkflowBuilder, newStep, this); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -519,7 +519,7 @@ public IStepBuilder Activity(string activityName, Expression step.EffectiveDate, effectiveDate); - Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } } diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 690a6a596..576c1b981 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -94,7 +94,7 @@ public async Task StartWorkflow(string workflowId, int? version, string id = await _persistenceStore.CreateNewWorkflow(wf); await _queueProvider.QueueWork(id, QueueType.Workflow); await _queueProvider.QueueWork(id, QueueType.Index); - await _eventHub.PublishNotification(new WorkflowStarted() + await _eventHub.PublishNotification(new WorkflowStarted { EventTimeUtc = _dateTimeProvider.UtcNow, Reference = reference, @@ -137,7 +137,7 @@ public async Task SuspendWorkflow(string workflowId) wf.Status = WorkflowStatus.Suspended; await _persistenceStore.PersistWorkflow(wf); await _queueProvider.QueueWork(workflowId, QueueType.Index); - await _eventHub.PublishNotification(new WorkflowSuspended() + await _eventHub.PublishNotification(new WorkflowSuspended { EventTimeUtc = _dateTimeProvider.UtcNow, Reference = wf.Reference, @@ -173,7 +173,7 @@ public async Task ResumeWorkflow(string workflowId) await _persistenceStore.PersistWorkflow(wf); requeue = true; await _queueProvider.QueueWork(workflowId, QueueType.Index); - await _eventHub.PublishNotification(new WorkflowResumed() + await _eventHub.PublishNotification(new WorkflowResumed { EventTimeUtc = _dateTimeProvider.UtcNow, Reference = wf.Reference, @@ -207,7 +207,7 @@ public async Task TerminateWorkflow(string workflowId) wf.Status = WorkflowStatus.Terminated; await _persistenceStore.PersistWorkflow(wf); await _queueProvider.QueueWork(workflowId, QueueType.Index); - await _eventHub.PublishNotification(new WorkflowTerminated() + await _eventHub.PublishNotification(new WorkflowTerminated { EventTimeUtc = _dateTimeProvider.UtcNow, Reference = wf.Reference, diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index c6241fd64..6ba2ce564 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -62,7 +62,7 @@ public async Task Execute(WorkflowInstance workflow, Can { _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); pointer.SleepUntil = _datetimeProvider.UtcNow.Add(_options.ErrorRetryInterval); - wfResult.Errors.Add(new ExecutionError() + wfResult.Errors.Add(new ExecutionError { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, @@ -82,7 +82,7 @@ public async Task Execute(WorkflowInstance workflow, Can catch (Exception ex) { _logger.LogError(ex, "Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); - wfResult.Errors.Add(new ExecutionError() + wfResult.Errors.Add(new ExecutionError { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, @@ -116,7 +116,7 @@ private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, Workfl if (pointer.Status != PointerStatus.Running) { pointer.Status = PointerStatus.Running; - _publisher.PublishNotification(new StepStarted() + _publisher.PublishNotification(new StepStarted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, @@ -138,7 +138,7 @@ private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, Workfl private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, ExecutionPointer pointer, WorkflowExecutorResult wfResult, WorkflowDefinition def, CancellationToken cancellationToken = default) { - IStepExecutionContext context = new StepExecutionContext() + IStepExecutionContext context = new StepExecutionContext { Workflow = workflow, Step = step, @@ -159,7 +159,7 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe { _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); pointer.SleepUntil = _datetimeProvider.UtcNow.Add(_options.ErrorRetryInterval); - wfResult.Errors.Add(new ExecutionError() + wfResult.Errors.Add(new ExecutionError { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, @@ -253,7 +253,7 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo await middlewareRunner.RunPostMiddleware(workflow, def); } - _publisher.PublishNotification(new WorkflowCompleted() + _publisher.PublishNotification(new WorkflowCompleted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, diff --git a/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs b/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs index 901bd28b7..0b3b348e4 100644 --- a/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs +++ b/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs @@ -39,7 +39,7 @@ public override ExecutionPipelineDirective InitForExecution(WorkflowExecutorResu executionPointer.EventName = "UserAction"; executionPointer.Active = false; - executorResult.Subscriptions.Add(new EventSubscription() + executorResult.Subscriptions.Add(new EventSubscription { WorkflowId = workflow.Id, StepId = executionPointer.StepId, diff --git a/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs b/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs index a1edd9b72..7772a894c 100644 --- a/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs +++ b/src/extensions/WorkflowCore.Users/Primitives/UserTask.cs @@ -50,7 +50,7 @@ public override ExecutionResult Run(IStepExecutionContext context) if (context.PersistenceData == null) { - var result = ExecutionResult.Branch(new List() { context.Item }, new ControlPersistenceData() { ChildrenActive = true }); + var result = ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); result.OutcomeValue = action.OutcomeValue; return result; } @@ -74,7 +74,7 @@ private void SetupEscalations(IStepExecutionContext context) { foreach (var esc in _escalations) { - context.Workflow.ExecutionPointers.Add(new ExecutionPointer() + context.Workflow.ExecutionPointers.Add(new ExecutionPointer { Active = true, Id = Guid.NewGuid().ToString(), diff --git a/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs b/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs index 44e9e1ce7..3330c123b 100644 --- a/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs +++ b/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs @@ -30,7 +30,7 @@ public static IStepBuilder UserStep(this ISte stepSetup.Invoke(stepBuilder); newStep.Name = newStep.Name ?? typeof(UserStepContainer).Name; - builder.Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + builder.Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } @@ -64,7 +64,7 @@ public static IUserTaskBuilder UserTask(this IStepBuild stepSetup.Invoke(stepBuilder); newStep.Name = newStep.Name ?? typeof(UserTask).Name; - builder.Step.Outcomes.Add(new ValueOutcome() { NextStep = newStep.Id }); + builder.Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); return stepBuilder; } diff --git a/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowHostExtensions.cs b/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowHostExtensions.cs index dbb44becc..89b753f52 100644 --- a/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowHostExtensions.cs +++ b/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowHostExtensions.cs @@ -15,7 +15,7 @@ public static class WorkflowHostExtensions { public static async Task PublishUserAction(this IWorkflowHost host, string actionKey, string user, object value) { - UserAction data = new UserAction() + UserAction data = new UserAction { User = user, OutcomeValue = value diff --git a/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowInstanceExtensions.cs b/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowInstanceExtensions.cs index de44c011b..464cedd14 100644 --- a/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowInstanceExtensions.cs +++ b/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowInstanceExtensions.cs @@ -14,7 +14,7 @@ public static IEnumerable GetOpenUserActions(this WorkflowInstan var pointers = workflow.ExecutionPointers.Where(x => !x.EventPublished && x.EventName == UserTask.EventName).ToList(); foreach (var pointer in pointers) { - var item = new OpenUserAction() + var item = new OpenUserAction { Key = pointer.EventKey, Prompt = Convert.ToString(pointer.ExtensionAttributes[UserTask.ExtPrompt]), diff --git a/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs b/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs index ce241ab52..5ea5da8db 100644 --- a/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs +++ b/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs @@ -53,7 +53,7 @@ public IUserTaskBuilder WithEscalation(Expression> var lastStep = WorkflowBuilder.LastStep; action.Invoke(WorkflowBuilder); if (WorkflowBuilder.LastStep > lastStep) - newStep.Outcomes.Add(new ValueOutcome() { NextStep = lastStep + 1 }); + newStep.Outcomes.Add(new ValueOutcome { NextStep = lastStep + 1 }); } return this; diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs index cf3f9183c..ae0659968 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs @@ -10,7 +10,7 @@ namespace WorkflowCore.Persistence.EntityFramework { internal static class ExtensionMethods { - private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; internal static PersistedWorkflow ToPersistable(this WorkflowInstance instance, PersistedWorkflow persistable = null) { diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs index 2cf6cab3e..dfa5f75fb 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/DataObjectSerializer.cs @@ -11,7 +11,7 @@ namespace WorkflowCore.Persistence.MongoDB.Services { public class DataObjectSerializer : SerializerBase { - private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects, }; diff --git a/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs index 6ec9b0f8b..62f9cf38e 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs @@ -12,7 +12,7 @@ namespace WorkflowCore.Providers.AWS { internal static class ModelExtensions { - private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; public static Dictionary ToDynamoMap(this WorkflowInstance source) { @@ -21,10 +21,10 @@ public static Dictionary ToDynamoMap(this WorkflowInstan result["id"] = new AttributeValue(source.Id); result["workflow_definition_id"] = new AttributeValue(source.WorkflowDefinitionId); result["version"] = new AttributeValue(source.Version.ToString()); - result["next_execution"] = new AttributeValue() { N = (source.NextExecution ?? 0).ToString() }; - result["create_time"] = new AttributeValue() { N = source.CreateTime.Ticks.ToString() }; + result["next_execution"] = new AttributeValue { N = (source.NextExecution ?? 0).ToString() }; + result["create_time"] = new AttributeValue { N = source.CreateTime.Ticks.ToString() }; result["data"] = new AttributeValue(JsonConvert.SerializeObject(source.Data, SerializerSettings)); - result["workflow_status"] = new AttributeValue() { N = Convert.ToInt32(source.Status).ToString() }; + result["workflow_status"] = new AttributeValue { N = Convert.ToInt32(source.Status).ToString() }; if (!string.IsNullOrEmpty(source.Description)) result["description"] = new AttributeValue(source.Description); @@ -33,7 +33,7 @@ public static Dictionary ToDynamoMap(this WorkflowInstan result["reference"] = new AttributeValue(source.Reference); if (source.CompleteTime.HasValue) - result["complete_time"] = new AttributeValue() { N = source.CompleteTime.Value.Ticks.ToString() }; + result["complete_time"] = new AttributeValue { N = source.CompleteTime.Value.Ticks.ToString() }; var pointers = new List(); foreach (var pointer in source.ExecutionPointers) @@ -41,17 +41,17 @@ public static Dictionary ToDynamoMap(this WorkflowInstan pointers.Add(new AttributeValue(JsonConvert.SerializeObject(pointer, SerializerSettings))); } - result["pointers"] = new AttributeValue() { L = pointers }; + result["pointers"] = new AttributeValue { L = pointers }; if (source.Status == WorkflowStatus.Runnable) - result["runnable"] = new AttributeValue() { N = 1.ToString() }; + result["runnable"] = new AttributeValue { N = 1.ToString() }; return result; } public static WorkflowInstance ToWorkflowInstance(this Dictionary source) { - var result = new WorkflowInstance() + var result = new WorkflowInstance { Id = source["id"].S, WorkflowDefinitionId = source["workflow_definition_id"].S, @@ -90,7 +90,7 @@ public static Dictionary ToDynamoMap(this EventSubscript ["workflow_id"] = new AttributeValue(source.WorkflowId), ["execution_pointer_id"] = new AttributeValue(source.ExecutionPointerId), ["step_id"] = new AttributeValue(source.StepId.ToString()), - ["subscribe_as_of"] = new AttributeValue() { N = source.SubscribeAsOf.Ticks.ToString() }, + ["subscribe_as_of"] = new AttributeValue { N = source.SubscribeAsOf.Ticks.ToString() }, ["subscription_data"] = new AttributeValue(JsonConvert.SerializeObject(source.SubscriptionData, SerializerSettings)), ["event_slug"] = new AttributeValue($"{source.EventName}:{source.EventKey}") }; @@ -101,14 +101,14 @@ public static Dictionary ToDynamoMap(this EventSubscript result["external_worker_id"] = new AttributeValue(source.ExternalWorkerId); if (source.ExternalTokenExpiry.HasValue) - result["external_token_expiry"] = new AttributeValue() { N = source.ExternalTokenExpiry.Value.Ticks.ToString()}; + result["external_token_expiry"] = new AttributeValue { N = source.ExternalTokenExpiry.Value.Ticks.ToString()}; return result; } public static EventSubscription ToEventSubscription(this Dictionary source) { - var result = new EventSubscription() + var result = new EventSubscription { Id = source["id"].S, EventName = source["event_name"].S, @@ -140,19 +140,19 @@ public static Dictionary ToDynamoMap(this Event source) ["event_name"] = new AttributeValue(source.EventName), ["event_key"] = new AttributeValue(source.EventKey), ["event_data"] = new AttributeValue(JsonConvert.SerializeObject(source.EventData, SerializerSettings)), - ["event_time"] = new AttributeValue() { N = source.EventTime.Ticks.ToString() }, + ["event_time"] = new AttributeValue { N = source.EventTime.Ticks.ToString() }, ["event_slug"] = new AttributeValue($"{source.EventName}:{source.EventKey}") }; if (!source.IsProcessed) - result["not_processed"] = new AttributeValue() { N = 1.ToString() }; + result["not_processed"] = new AttributeValue { N = 1.ToString() }; return result; } public static Event ToEvent(this Dictionary source) { - var result = new Event() + var result = new Event { Id = source["id"].S, EventName = source["event_name"].S, diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs index a78b006c0..a6bb72662 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs @@ -33,10 +33,10 @@ public Task ProvisionTables() private async Task CreateWorkflowTable() { - var runnableIndex = new GlobalSecondaryIndex() + var runnableIndex = new GlobalSecondaryIndex { IndexName = "ix_runnable", - KeySchema = new List() + KeySchema = new List { { new KeySchemaElement @@ -53,33 +53,33 @@ private async Task CreateWorkflowTable() } } }, - Projection = new Projection() + Projection = new Projection { ProjectionType = ProjectionType.KEYS_ONLY }, - ProvisionedThroughput = new ProvisionedThroughput() + ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = 1, WriteCapacityUnits = 1 } }; - var createRequest = new CreateTableRequest($"{_tablePrefix}-{DynamoPersistenceProvider.WORKFLOW_TABLE}", new List() + var createRequest = new CreateTableRequest($"{_tablePrefix}-{DynamoPersistenceProvider.WORKFLOW_TABLE}", new List { new KeySchemaElement("id", KeyType.HASH) }) { - AttributeDefinitions = new List() + AttributeDefinitions = new List { new AttributeDefinition("id", ScalarAttributeType.S), new AttributeDefinition("runnable", ScalarAttributeType.N), new AttributeDefinition("next_execution", ScalarAttributeType.N), }, - GlobalSecondaryIndexes = new List() + GlobalSecondaryIndexes = new List { runnableIndex }, - ProvisionedThroughput = new ProvisionedThroughput() + ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = 1, WriteCapacityUnits = 1 @@ -91,10 +91,10 @@ private async Task CreateWorkflowTable() private async Task CreateSubscriptionTable() { - var slugIndex = new GlobalSecondaryIndex() + var slugIndex = new GlobalSecondaryIndex { IndexName = "ix_slug", - KeySchema = new List() + KeySchema = new List { { new KeySchemaElement @@ -111,33 +111,33 @@ private async Task CreateSubscriptionTable() } } }, - Projection = new Projection() + Projection = new Projection { ProjectionType = ProjectionType.ALL }, - ProvisionedThroughput = new ProvisionedThroughput() + ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = 1, WriteCapacityUnits = 1 } }; - var createRequest = new CreateTableRequest($"{_tablePrefix}-{DynamoPersistenceProvider.SUBCRIPTION_TABLE}", new List() + var createRequest = new CreateTableRequest($"{_tablePrefix}-{DynamoPersistenceProvider.SUBCRIPTION_TABLE}", new List { new KeySchemaElement("id", KeyType.HASH) }) { - AttributeDefinitions = new List() + AttributeDefinitions = new List { new AttributeDefinition("id", ScalarAttributeType.S), new AttributeDefinition("event_slug", ScalarAttributeType.S), new AttributeDefinition("subscribe_as_of", ScalarAttributeType.N) }, - GlobalSecondaryIndexes = new List() + GlobalSecondaryIndexes = new List { slugIndex }, - ProvisionedThroughput = new ProvisionedThroughput() + ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = 1, WriteCapacityUnits = 1 @@ -149,10 +149,10 @@ private async Task CreateSubscriptionTable() private async Task CreateEventTable() { - var slugIndex = new GlobalSecondaryIndex() + var slugIndex = new GlobalSecondaryIndex { IndexName = "ix_slug", - KeySchema = new List() + KeySchema = new List { { new KeySchemaElement @@ -169,21 +169,21 @@ private async Task CreateEventTable() } } }, - Projection = new Projection() + Projection = new Projection { ProjectionType = ProjectionType.KEYS_ONLY }, - ProvisionedThroughput = new ProvisionedThroughput() + ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = 1, WriteCapacityUnits = 1 } }; - var processedIndex = new GlobalSecondaryIndex() + var processedIndex = new GlobalSecondaryIndex { IndexName = "ix_not_processed", - KeySchema = new List() + KeySchema = new List { { new KeySchemaElement @@ -200,35 +200,35 @@ private async Task CreateEventTable() } } }, - Projection = new Projection() + Projection = new Projection { ProjectionType = ProjectionType.KEYS_ONLY }, - ProvisionedThroughput = new ProvisionedThroughput() + ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = 1, WriteCapacityUnits = 1 } }; - var createRequest = new CreateTableRequest($"{_tablePrefix}-{DynamoPersistenceProvider.EVENT_TABLE}", new List() + var createRequest = new CreateTableRequest($"{_tablePrefix}-{DynamoPersistenceProvider.EVENT_TABLE}", new List { new KeySchemaElement("id", KeyType.HASH) }) { - AttributeDefinitions = new List() + AttributeDefinitions = new List { new AttributeDefinition("id", ScalarAttributeType.S), new AttributeDefinition("not_processed", ScalarAttributeType.N), new AttributeDefinition("event_slug", ScalarAttributeType.S), new AttributeDefinition("event_time", ScalarAttributeType.N) }, - GlobalSecondaryIndexes = new List() + GlobalSecondaryIndexes = new List { slugIndex, processedIndex }, - ProvisionedThroughput = new ProvisionedThroughput() + ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = 1, WriteCapacityUnits = 1 diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs index b10dc4365..6f4aca0e8 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -39,14 +39,14 @@ public async Task AcquireLock(string Id, CancellationToken cancellationTok { try { - var req = new PutItemRequest() + var req = new PutItemRequest { TableName = _tableName, Item = new Dictionary { { "id", new AttributeValue(Id) }, { "lock_owner", new AttributeValue(_nodeId) }, - { "expires", new AttributeValue() + { "expires", new AttributeValue { N = Convert.ToString(new DateTimeOffset(_dateTimeProvider.UtcNow).ToUnixTimeMilliseconds() + _ttl) } @@ -55,7 +55,7 @@ public async Task AcquireLock(string Id, CancellationToken cancellationTok ConditionExpression = "attribute_not_exists(id) OR (expires < :expired)", ExpressionAttributeValues = new Dictionary { - { ":expired", new AttributeValue() + { ":expired", new AttributeValue { N = Convert.ToString(new DateTimeOffset(_dateTimeProvider.UtcNow).ToUnixTimeMilliseconds() + _jitter) } @@ -91,7 +91,7 @@ public async Task ReleaseLock(string Id) try { - var req = new DeleteItemRequest() + var req = new DeleteItemRequest { TableName = _tableName, Key = new Dictionary @@ -154,7 +154,7 @@ private async void SendHeartbeat() { { "id", new AttributeValue(item) }, { "lock_owner", new AttributeValue(_nodeId) }, - { "expires", new AttributeValue() + { "expires", new AttributeValue { N = Convert.ToString(new DateTimeOffset(_dateTimeProvider.UtcNow).ToUnixTimeMilliseconds() + _ttl) } @@ -204,12 +204,12 @@ private async Task EnsureTable() private async Task CreateTable() { - var createRequest = new CreateTableRequest(_tableName, new List() + var createRequest = new CreateTableRequest(_tableName, new List { new KeySchemaElement("id", KeyType.HASH) }) { - AttributeDefinitions = new List() + AttributeDefinitions = new List { new AttributeDefinition("id", ScalarAttributeType.S) }, diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index afc531b7a..69ba8eaf1 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -36,7 +36,7 @@ public async Task CreateNewWorkflow(WorkflowInstance workflow) { workflow.Id = Guid.NewGuid().ToString(); - var req = new PutItemRequest() + var req = new PutItemRequest { TableName = $"{_tablePrefix}-{WORKFLOW_TABLE}", Item = workflow.ToDynamoMap(), @@ -50,7 +50,7 @@ public async Task CreateNewWorkflow(WorkflowInstance workflow) public async Task PersistWorkflow(WorkflowInstance workflow) { - var request = new PutItemRequest() + var request = new PutItemRequest { TableName = $"{_tablePrefix}-{WORKFLOW_TABLE}", Item = workflow.ToDynamoMap() @@ -64,7 +64,7 @@ public async Task> GetRunnableInstances(DateTime asAt) var result = new List(); var now = asAt.ToUniversalTime().Ticks; - var request = new QueryRequest() + var request = new QueryRequest { TableName = $"{_tablePrefix}-{WORKFLOW_TABLE}", IndexName = "ix_runnable", @@ -73,13 +73,13 @@ public async Task> GetRunnableInstances(DateTime asAt) ExpressionAttributeValues = new Dictionary { { - ":r", new AttributeValue() + ":r", new AttributeValue { N = 1.ToString() } }, { - ":effective_date", new AttributeValue() + ":effective_date", new AttributeValue { N = Convert.ToString(now) } @@ -105,7 +105,7 @@ public Task> GetWorkflowInstances(WorkflowStatus? public async Task GetWorkflowInstance(string Id) { - var req = new GetItemRequest() + var req = new GetItemRequest { TableName = $"{_tablePrefix}-{WORKFLOW_TABLE}", Key = new Dictionary @@ -125,10 +125,10 @@ public async Task> GetWorkflowInstances(IEnumerabl return new List(); } - var keys = new KeysAndAttributes() { Keys = new List>() }; + var keys = new KeysAndAttributes { Keys = new List>() }; foreach (var id in ids) { - var key = new Dictionary() + var key = new Dictionary { { "id", new AttributeValue { S = id } @@ -139,7 +139,7 @@ public async Task> GetWorkflowInstances(IEnumerabl var request = new BatchGetItemRequest { - RequestItems = new Dictionary() + RequestItems = new Dictionary { { $"{_tablePrefix}-{WORKFLOW_TABLE}", keys @@ -165,7 +165,7 @@ public async Task CreateEventSubscription(EventSubscription subscription { subscription.Id = Guid.NewGuid().ToString(); - var req = new PutItemRequest() + var req = new PutItemRequest { TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", Item = subscription.ToDynamoMap(), @@ -182,7 +182,7 @@ public async Task> GetSubscriptions(string eventN var result = new List(); var asOfTicks = asOf.ToUniversalTime().Ticks; - var request = new QueryRequest() + var request = new QueryRequest { TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", IndexName = "ix_slug", @@ -194,7 +194,7 @@ public async Task> GetSubscriptions(string eventN ":slug", new AttributeValue($"{eventName}:{eventKey}") }, { - ":as_of", new AttributeValue() + ":as_of", new AttributeValue { N = Convert.ToString(asOfTicks) } @@ -215,7 +215,7 @@ public async Task> GetSubscriptions(string eventN public async Task TerminateSubscription(string eventSubscriptionId) { - var request = new DeleteItemRequest() + var request = new DeleteItemRequest { TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", Key = new Dictionary @@ -230,7 +230,7 @@ public async Task CreateEvent(Event newEvent) { newEvent.Id = Guid.NewGuid().ToString(); - var req = new PutItemRequest() + var req = new PutItemRequest { TableName = $"{_tablePrefix}-{EVENT_TABLE}", Item = newEvent.ToDynamoMap(), @@ -244,7 +244,7 @@ public async Task CreateEvent(Event newEvent) public async Task GetEvent(string id) { - var req = new GetItemRequest() + var req = new GetItemRequest { TableName = $"{_tablePrefix}-{EVENT_TABLE}", Key = new Dictionary @@ -262,7 +262,7 @@ public async Task> GetRunnableEvents(DateTime asAt) var result = new List(); var now = asAt.ToUniversalTime().Ticks; - var request = new QueryRequest() + var request = new QueryRequest { TableName = $"{_tablePrefix}-{EVENT_TABLE}", IndexName = "ix_not_processed", @@ -270,9 +270,9 @@ public async Task> GetRunnableEvents(DateTime asAt) KeyConditionExpression = "not_processed = :n and event_time <= :effectiveDate", ExpressionAttributeValues = new Dictionary { - { ":n" , new AttributeValue() { N = 1.ToString() } }, + { ":n" , new AttributeValue { N = 1.ToString() } }, { - ":effectiveDate", new AttributeValue() + ":effectiveDate", new AttributeValue { N = Convert.ToString(now) } @@ -296,7 +296,7 @@ public async Task> GetEvents(string eventName, string eventK var result = new List(); var asOfTicks = asOf.ToUniversalTime().Ticks; - var request = new QueryRequest() + var request = new QueryRequest { TableName = $"{_tablePrefix}-{EVENT_TABLE}", IndexName = "ix_slug", @@ -308,7 +308,7 @@ public async Task> GetEvents(string eventName, string eventK ":slug", new AttributeValue($"{eventName}:{eventKey}") }, { - ":effective_date", new AttributeValue() + ":effective_date", new AttributeValue { N = Convert.ToString(asOfTicks) } @@ -329,7 +329,7 @@ public async Task> GetEvents(string eventName, string eventK public async Task MarkEventProcessed(string id) { - var request = new UpdateItemRequest() + var request = new UpdateItemRequest { TableName = $"{_tablePrefix}-{EVENT_TABLE}", Key = new Dictionary @@ -343,7 +343,7 @@ public async Task MarkEventProcessed(string id) public async Task MarkEventUnprocessed(string id) { - var request = new UpdateItemRequest() + var request = new UpdateItemRequest { TableName = $"{_tablePrefix}-{EVENT_TABLE}", Key = new Dictionary @@ -351,9 +351,9 @@ public async Task MarkEventUnprocessed(string id) { "id", new AttributeValue(id) } }, UpdateExpression = "ADD not_processed = :n", - ExpressionAttributeValues = new Dictionary() + ExpressionAttributeValues = new Dictionary { - { ":n" , new AttributeValue() { N = 1.ToString() } } + { ":n" , new AttributeValue { N = 1.ToString() } } } }; await _client.UpdateItemAsync(request); @@ -372,7 +372,7 @@ public void EnsureStoreExists() public async Task GetSubscription(string eventSubscriptionId) { - var req = new GetItemRequest() + var req = new GetItemRequest { TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", Key = new Dictionary @@ -390,7 +390,7 @@ public async Task GetFirstOpenSubscription(string eventName, var result = new List(); var asOfTicks = asOf.ToUniversalTime().Ticks; - var request = new QueryRequest() + var request = new QueryRequest { TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", IndexName = "ix_slug", @@ -404,7 +404,7 @@ public async Task GetFirstOpenSubscription(string eventName, ":slug", new AttributeValue($"{eventName}:{eventKey}") }, { - ":as_of", new AttributeValue() + ":as_of", new AttributeValue { N = Convert.ToString(asOfTicks) } @@ -423,7 +423,7 @@ public async Task GetFirstOpenSubscription(string eventName, public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) { - var request = new UpdateItemRequest() + var request = new UpdateItemRequest { TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", Key = new Dictionary @@ -432,11 +432,11 @@ public async Task SetSubscriptionToken(string eventSubscriptionId, string }, UpdateExpression = "SET external_token = :external_token, external_worker_id = :external_worker_id, external_token_expiry = :external_token_expiry", ConditionExpression = "attribute_not_exists(external_token)", - ExpressionAttributeValues = new Dictionary() + ExpressionAttributeValues = new Dictionary { - { ":external_token" , new AttributeValue() { S = token } }, - { ":external_worker_id" , new AttributeValue() { S = workerId } }, - { ":external_token_expiry" , new AttributeValue() { N = expiry.Ticks.ToString() } } + { ":external_token" , new AttributeValue { S = token } }, + { ":external_worker_id" , new AttributeValue { S = workerId } }, + { ":external_token_expiry" , new AttributeValue { N = expiry.Ticks.ToString() } } } }; try @@ -452,7 +452,7 @@ public async Task SetSubscriptionToken(string eventSubscriptionId, string public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) { - var request = new UpdateItemRequest() + var request = new UpdateItemRequest { TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", Key = new Dictionary @@ -461,9 +461,9 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke }, UpdateExpression = "REMOVE external_token, external_worker_id, external_token_expiry", ConditionExpression = "external_token = :external_token", - ExpressionAttributeValues = new Dictionary() + ExpressionAttributeValues = new Dictionary { - { ":external_token" , new AttributeValue() { S = token } }, + { ":external_token" , new AttributeValue { S = token } }, } }; diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs index 7db21aab7..5b0830ce0 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs @@ -46,7 +46,7 @@ public async Task PublishNotification(LifeCycleEvent evt) _serializer.Serialize(writer, evt); writer.Flush(); - var response = await _client.PutRecordAsync(new PutRecordRequest() + var response = await _client.PutRecordAsync(new PutRecordRequest { StreamName = _streamName, PartitionKey = evt.WorkflowInstanceId, @@ -93,7 +93,7 @@ private async Task EnsureStream() { try { - await _client.DescribeStreamSummaryAsync(new DescribeStreamSummaryRequest() + await _client.DescribeStreamSummaryAsync(new DescribeStreamSummaryRequest { StreamName = _streamName }); @@ -106,7 +106,7 @@ await _client.DescribeStreamSummaryAsync(new DescribeStreamSummaryRequest() private async Task CreateStream() { - await _client.CreateStreamAsync(new CreateStreamRequest() + await _client.CreateStreamAsync(new CreateStreamRequest { StreamName = _streamName, ShardCount = _defaultShardCount @@ -117,7 +117,7 @@ await _client.CreateStreamAsync(new CreateStreamRequest() { i++; await Task.Delay(3000); - var poll = await _client.DescribeStreamSummaryAsync(new DescribeStreamSummaryRequest() + var poll = await _client.DescribeStreamSummaryAsync(new DescribeStreamSummaryRequest { StreamName = _streamName }); diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs index 2a3a42494..799125a0d 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs @@ -38,14 +38,14 @@ public KinesisStreamConsumer(AWSCredentials credentials, RegionEndpoint region, public async Task Subscribe(string appName, string stream, Action action) { - var shards = await _client.ListShardsAsync(new ListShardsRequest() + var shards = await _client.ListShardsAsync(new ListShardsRequest { StreamName = stream }); foreach (var shard in shards.Shards) { - _subscribers.Add(new ShardSubscription() + _subscribers.Add(new ShardSubscription { AppName = appName, Stream = stream, @@ -117,7 +117,7 @@ private async Task GetBatch(ShardSubscription sub) if (iterator == null) { - var iterResp = await _client.GetShardIteratorAsync(new GetShardIteratorRequest() + var iterResp = await _client.GetShardIteratorAsync(new GetShardIteratorRequest { ShardId = sub.Shard.ShardId, StreamName = sub.Stream, @@ -129,7 +129,7 @@ private async Task GetBatch(ShardSubscription sub) try { - var result = await _client.GetRecordsAsync(new GetRecordsRequest() + var result = await _client.GetRecordsAsync(new GetRecordsRequest { ShardIterator = iterator, Limit = _batchSize @@ -140,7 +140,7 @@ private async Task GetBatch(ShardSubscription sub) catch (ExpiredIteratorException) { var lastSequence = await _tracker.GetNextLastSequenceNumber(sub.AppName, sub.Stream, sub.Shard.ShardId); - var iterResp = await _client.GetShardIteratorAsync(new GetShardIteratorRequest() + var iterResp = await _client.GetShardIteratorAsync(new GetShardIteratorRequest { ShardId = sub.Shard.ShardId, StreamName = sub.Stream, @@ -149,7 +149,7 @@ private async Task GetBatch(ShardSubscription sub) }); iterator = iterResp.ShardIterator; - var result = await _client.GetRecordsAsync(new GetRecordsRequest() + var result = await _client.GetRecordsAsync(new GetRecordsRequest { ShardIterator = iterator, Limit = _batchSize diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs index 5f4abc2e1..8d6a04b8e 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs @@ -31,7 +31,7 @@ public async Task GetNextShardIterator(string app, string stream, string if (!_tableConfirmed) await EnsureTable(); - var response = await _client.GetItemAsync(new GetItemRequest() + var response = await _client.GetItemAsync(new GetItemRequest { TableName = _tableName, Key = new Dictionary @@ -51,7 +51,7 @@ public async Task GetNextLastSequenceNumber(string app, string stream, s if (!_tableConfirmed) await EnsureTable(); - var response = await _client.GetItemAsync(new GetItemRequest() + var response = await _client.GetItemAsync(new GetItemRequest { TableName = _tableName, Key = new Dictionary @@ -71,7 +71,7 @@ public async Task IncrementShardIterator(string app, string stream, string shard if (!_tableConfirmed) await EnsureTable(); - await _client.UpdateItemAsync(new UpdateItemRequest() + await _client.UpdateItemAsync(new UpdateItemRequest { TableName = _tableName, Key = new Dictionary @@ -79,7 +79,7 @@ await _client.UpdateItemAsync(new UpdateItemRequest() {"id", new AttributeValue(FormatId(app, stream, shard))} }, UpdateExpression = "SET next_iterator = :n", - ExpressionAttributeValues = new Dictionary() + ExpressionAttributeValues = new Dictionary { { ":n" , new AttributeValue(iterator) } } @@ -91,7 +91,7 @@ public async Task IncrementShardIteratorAndSequence(string app, string stream, s if (!_tableConfirmed) await EnsureTable(); - await _client.PutItemAsync(new PutItemRequest() + await _client.PutItemAsync(new PutItemRequest { TableName = _tableName, Item = new Dictionary @@ -118,12 +118,12 @@ private async Task EnsureTable() private async Task CreateTable() { - var createRequest = new CreateTableRequest(_tableName, new List() + var createRequest = new CreateTableRequest(_tableName, new List { new KeySchemaElement("id", KeyType.HASH) }) { - AttributeDefinitions = new List() + AttributeDefinitions = new List { new AttributeDefinition("id", ScalarAttributeType.S) }, diff --git a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs index 341f7b71b..85579f7fc 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs @@ -19,11 +19,11 @@ public class PersistedEvent public bool IsProcessed { get; set; } - private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; public static PersistedEvent FromInstance(Event instance) { - return new PersistedEvent() + return new PersistedEvent { id = instance.Id, EventKey = instance.EventKey, @@ -36,7 +36,7 @@ public static PersistedEvent FromInstance(Event instance) public static Event ToInstance(PersistedEvent instance) { - return new Event() + return new Event { Id = instance.id, EventKey = instance.EventKey, diff --git a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs index d0660046c..55482e17d 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs @@ -29,11 +29,11 @@ public class PersistedSubscription public DateTime? ExternalTokenExpiry { get; set; } - private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; public static PersistedSubscription FromInstance(EventSubscription instance) { - return new PersistedSubscription() + return new PersistedSubscription { id = instance.Id, EventKey = instance.EventKey, @@ -51,7 +51,7 @@ public static PersistedSubscription FromInstance(EventSubscription instance) public static EventSubscription ToInstance(PersistedSubscription instance) { - return new EventSubscription() + return new EventSubscription { Id = instance.id, EventKey = instance.EventKey, diff --git a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs index c9f565592..bd480fb71 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs @@ -29,11 +29,11 @@ public class PersistedWorkflow public DateTime? CompleteTime { get; set; } - private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; public static PersistedWorkflow FromInstance(WorkflowInstance instance) { - var result = new PersistedWorkflow() + var result = new PersistedWorkflow { id = instance.Id, CompleteTime = instance.CompleteTime, @@ -53,7 +53,7 @@ public static PersistedWorkflow FromInstance(WorkflowInstance instance) public static WorkflowInstance ToInstance(PersistedWorkflow instance) { - var result = new WorkflowInstance() + var result = new WorkflowInstance { Id = instance.id, CompleteTime = instance.CompleteTime, diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs index 001ade0a0..ff1c2e187 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs @@ -21,8 +21,8 @@ public async Task Provision(string dbId) { var dbResp = await _client.CreateDatabaseIfNotExistsAsync(dbId); var wfIndexPolicy = new IndexingPolicy(); - wfIndexPolicy.IncludedPaths.Add(new IncludedPath() { Path = @"/*" }); - wfIndexPolicy.ExcludedPaths.Add(new ExcludedPath() { Path = @"/ExecutionPointers/?" }); + wfIndexPolicy.IncludedPaths.Add(new IncludedPath { Path = @"/*" }); + wfIndexPolicy.ExcludedPaths.Add(new ExcludedPath { Path = @"/ExecutionPointers/?" }); Task.WaitAll( dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(CosmosDbPersistenceProvider.WorkflowContainerName, @"/id") diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/Models/WorkflowSearchModel.cs b/src/providers/WorkflowCore.Providers.Elasticsearch/Models/WorkflowSearchModel.cs index 91c4838f7..1399bdd23 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/Models/WorkflowSearchModel.cs +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/Models/WorkflowSearchModel.cs @@ -39,7 +39,7 @@ public class WorkflowSearchModel public WorkflowSearchResult ToSearchResult() { - var result = new WorkflowSearchResult() + var result = new WorkflowSearchResult { Id = Id, CompleteTime = CompleteTime, @@ -88,7 +88,7 @@ public static WorkflowSearchModel FromWorkflowInstance(WorkflowInstance workflow { if (ep.Status == PointerStatus.Sleeping) { - result.SleepingSteps.Add(new StepInfo() + result.SleepingSteps.Add(new StepInfo { StepId = ep.StepId, Name = ep.StepName @@ -97,7 +97,7 @@ public static WorkflowSearchModel FromWorkflowInstance(WorkflowInstance workflow if (ep.Status == PointerStatus.WaitingForEvent) { - result.WaitingSteps.Add(new StepInfo() + result.WaitingSteps.Add(new StepInfo { StepId = ep.StepId, Name = ep.StepName @@ -106,7 +106,7 @@ public static WorkflowSearchModel FromWorkflowInstance(WorkflowInstance workflow if (ep.Status == PointerStatus.Failed) { - result.FailedSteps.Add(new StepInfo() + result.FailedSteps.Add(new StepInfo { StepId = ep.StepId, Name = ep.StepName diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs index 8d7d1e35d..2cf12e1cc 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs @@ -16,7 +16,7 @@ public class RedisLifeCycleEventHub : ILifeCycleEventHub private readonly string _connectionString; private readonly string _channel; private ICollection> _subscribers = new HashSet>(); - private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; private IConnectionMultiplexer _multiplexer; private ISubscriber _subscriber; diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs index 1b18e7539..006f531d6 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs @@ -70,7 +70,7 @@ public Task ReleaseLock(string Id) public async Task Start() { _multiplexer = await ConnectionMultiplexer.ConnectAsync(_connectionString); - _redlockFactory = RedLockFactory.Create(new List() { new RedLockMultiplexer(_multiplexer) }); + _redlockFactory = RedLockFactory.Create(new List { new RedLockMultiplexer(_multiplexer) }); } public async Task Stop() diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index 3e593a550..66ca58d72 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -24,7 +24,7 @@ public class RedisPersistenceProvider : IPersistenceProvider private readonly IConnectionMultiplexer _multiplexer; private readonly IDatabase _redis; - private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; private readonly bool _removeComplete; public RedisPersistenceProvider(string connectionString, string prefix, bool removeComplete, ILoggerFactory logFactory) diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs index dbed9fa98..1281bfbdf 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs @@ -18,7 +18,7 @@ public class RedisQueueProvider : IQueueProvider private IConnectionMultiplexer _multiplexer; private IDatabase _redis; - private readonly Dictionary _queues = new Dictionary() + private readonly Dictionary _queues = new Dictionary { [QueueType.Workflow] = "workflows", [QueueType.Event] = "events", diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs index d9ec3bbeb..8a84f99c6 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs @@ -22,7 +22,7 @@ public class RabbitMQProvider : IQueueProvider private readonly IServiceProvider _serviceProvider; private IConnection _connection = null; - private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; public bool IsDequeueBlocking => false; diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index 7e2364408..ada9b580a 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -26,7 +26,7 @@ public static WorkflowOptions UseSqlServerBroker(this WorkflowOptions options, s options.Services.AddTransient(); options.Services.AddTransient(sp => new SqlServerQueueProviderMigrator(connectionString, sp.GetService(), sp.GetService())); - var sqlOptions = new SqlServerQueueProviderOptions() + var sqlOptions = new SqlServerQueueProviderOptions { ConnectionString = connectionString, CanCreateDb = canCreateDb, diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs index 691e6561b..9e54393a1 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs @@ -17,7 +17,7 @@ namespace WorkflowCore.QueueProviders.SqlServer.Services /// public class QueueConfigProvider : IQueueConfigProvider { - private readonly Dictionary _queues = new Dictionary() + private readonly Dictionary _queues = new Dictionary { [QueueType.Workflow] = new QueueConfig("workflow"), [QueueType.Event] = new QueueConfig("event"), diff --git a/src/samples/WorkflowCore.Sample09/ForEachWorkflow.cs b/src/samples/WorkflowCore.Sample09/ForEachWorkflow.cs index 4e525c3de..e9a5cd937 100644 --- a/src/samples/WorkflowCore.Sample09/ForEachWorkflow.cs +++ b/src/samples/WorkflowCore.Sample09/ForEachWorkflow.cs @@ -15,7 +15,7 @@ public void Build(IWorkflowBuilder builder) { builder .StartWith() - .ForEach(data => new List() { 1, 2, 3, 4 }) + .ForEach(data => new List { 1, 2, 3, 4 }) .Do(x => x .StartWith() .Input(step => step.Item, (data, context) => context.Item) diff --git a/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs b/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs index 685b69ed1..879156d06 100644 --- a/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs +++ b/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs @@ -15,7 +15,7 @@ public void Build(IWorkflowBuilder builder) { builder .StartWith() - .ForEach(data => new List() { 1, 2, 3, 4 }, data => false) + .ForEach(data => new List { 1, 2, 3, 4 }, data => false) .Do(x => x .StartWith() .Input(step => step.Item, (data, context) => context.Item) diff --git a/src/samples/WorkflowCore.Sample10/Program.cs b/src/samples/WorkflowCore.Sample10/Program.cs index 75173f70c..aa401baa2 100644 --- a/src/samples/WorkflowCore.Sample10/Program.cs +++ b/src/samples/WorkflowCore.Sample10/Program.cs @@ -17,7 +17,7 @@ public static void Main(string[] args) host.Start(); Console.WriteLine("Starting workflow..."); - string workflowId = host.StartWorkflow("While", new MyData() { Counter = 0 }).Result; + string workflowId = host.StartWorkflow("While", new MyData { Counter = 0 }).Result; Console.ReadLine(); diff --git a/src/samples/WorkflowCore.Sample11/Program.cs b/src/samples/WorkflowCore.Sample11/Program.cs index f16853aa4..e409b400a 100644 --- a/src/samples/WorkflowCore.Sample11/Program.cs +++ b/src/samples/WorkflowCore.Sample11/Program.cs @@ -17,7 +17,7 @@ public static void Main(string[] args) host.Start(); Console.WriteLine("Starting workflow..."); - string workflowId = host.StartWorkflow("if-sample", new MyData() { Counter = 4 }).Result; + string workflowId = host.StartWorkflow("if-sample", new MyData { Counter = 4 }).Result; Console.ReadLine(); host.Stop(); diff --git a/src/samples/WorkflowCore.Sample12/Program.cs b/src/samples/WorkflowCore.Sample12/Program.cs index 81f50021d..937265e4f 100644 --- a/src/samples/WorkflowCore.Sample12/Program.cs +++ b/src/samples/WorkflowCore.Sample12/Program.cs @@ -17,7 +17,7 @@ public static void Main(string[] args) host.Start(); Console.WriteLine("Starting workflow..."); - host.StartWorkflow("outcome-sample", new MyData() { Value = 2 }); + host.StartWorkflow("outcome-sample", new MyData { Value = 2 }); Console.ReadLine(); diff --git a/src/samples/WorkflowCore.Sample18/Program.cs b/src/samples/WorkflowCore.Sample18/Program.cs index 8bf555690..295701499 100644 --- a/src/samples/WorkflowCore.Sample18/Program.cs +++ b/src/samples/WorkflowCore.Sample18/Program.cs @@ -20,7 +20,7 @@ static void Main(string[] args) Console.WriteLine("Starting workflow..."); - var workflowId = host.StartWorkflow("activity-sample", new MyData() { Request = "Spend $1,000,000" }).Result; + var workflowId = host.StartWorkflow("activity-sample", new MyData { Request = "Spend $1,000,000" }).Result; var approval = host.GetPendingActivity("get-approval", "worker1", TimeSpan.FromMinutes(1)).Result; diff --git a/src/samples/WorkflowCore.TestSample01/NUnitTest.cs b/src/samples/WorkflowCore.TestSample01/NUnitTest.cs index cec26fe9e..d01aaf5cb 100644 --- a/src/samples/WorkflowCore.TestSample01/NUnitTest.cs +++ b/src/samples/WorkflowCore.TestSample01/NUnitTest.cs @@ -21,7 +21,7 @@ protected override void Setup(bool registerClassMap = false) [Test] public void NUnit_workflow_test_sample() { - var workflowId = StartWorkflow(new MyDataClass() { Value1 = 2, Value2 = 3 }); + var workflowId = StartWorkflow(new MyDataClass { Value1 = 2, Value2 = 3 }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); diff --git a/src/samples/WorkflowCore.TestSample01/xUnitTest.cs b/src/samples/WorkflowCore.TestSample01/xUnitTest.cs index 6aeca8cdc..7afb636f0 100644 --- a/src/samples/WorkflowCore.TestSample01/xUnitTest.cs +++ b/src/samples/WorkflowCore.TestSample01/xUnitTest.cs @@ -17,7 +17,7 @@ public xUnitTest() [Fact] public void MyWorkflow() { - var workflowId = StartWorkflow(new MyDataClass() { Value1 = 2, Value2 = 3 }); + var workflowId = StartWorkflow(new MyDataClass { Value1 = 2, Value2 = 3 }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); diff --git a/test/Docker.Testify/DockerSetup.cs b/test/Docker.Testify/DockerSetup.cs index 7d5ae9bc3..1f6b48b07 100644 --- a/test/Docker.Testify/DockerSetup.cs +++ b/test/Docker.Testify/DockerSetup.cs @@ -60,7 +60,7 @@ public async Task StartContainer() await PullImage(ImageName, ImageTag); - var container = await docker.Containers.CreateContainerAsync(new CreateContainerParameters() + var container = await docker.Containers.CreateContainerAsync(new CreateContainerParameters { Image = $"{ImageName}:{ImageTag}", Name = $"{ContainerPrefix}-{Guid.NewGuid()}", @@ -113,13 +113,13 @@ public async Task PullImage(string name, string tag) return; Debug.WriteLine($"Pulling docker image {name}:{tag}"); - await docker.Images.CreateImageAsync(new ImagesCreateParameters() { FromImage = name, Tag = tag }, null, new Progress()); + await docker.Images.CreateImageAsync(new ImagesCreateParameters { FromImage = name, Tag = tag }, null, new Progress()); } public void Dispose() { docker.Containers.KillContainerAsync(containerId, new ContainerKillParameters()).Wait(); - docker.Containers.RemoveContainerAsync(containerId, new ContainerRemoveParameters() { Force = true }).Wait(); + docker.Containers.RemoveContainerAsync(containerId, new ContainerRemoveParameters { Force = true }).Wait(); } private int GetFreePort() diff --git a/test/ScratchPad/ElasticTest.cs b/test/ScratchPad/ElasticTest.cs index 0e57c4012..6d8ae43f0 100644 --- a/test/ScratchPad/ElasticTest.cs +++ b/test/ScratchPad/ElasticTest.cs @@ -30,10 +30,10 @@ public static void test(string[] args) host.RegisterWorkflow(); host.Start(); - var data1 = new WorkflowCore.Sample03.MyDataClass() { Value1 = 2, Value2 = 3 }; + var data1 = new WorkflowCore.Sample03.MyDataClass { Value1 = 2, Value2 = 3 }; host.StartWorkflow("PassingDataWorkflow", data1, "quick dog").Wait(); - var data2 = new WorkflowCore.Sample04.MyDataClass() { Value1 = "test" }; + var data2 = new WorkflowCore.Sample04.MyDataClass { Value1 = "test" }; host.StartWorkflow("EventSampleWorkflow", data2, "alt1 boom").Wait(); diff --git a/test/ScratchPad/Program.cs b/test/ScratchPad/Program.cs index 08f383712..3c0ee5f76 100644 --- a/test/ScratchPad/Program.cs +++ b/test/ScratchPad/Program.cs @@ -40,7 +40,7 @@ public static void Main(string[] args) for (var i = 0; i < 12000; i++) { - var wid = host.StartWorkflow("Test01", 1, new WfData() { Value1 = "two", Value2 = "data2" }).Result; + var wid = host.StartWorkflow("Test01", 1, new WfData { Value1 = "two", Value2 = "data2" }).Result; ids.Add(wid); } diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs index 03562bfbd..ae7b372ab 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs @@ -51,13 +51,13 @@ public ActivityScenario() [Fact] public void Scenario() { - var workflowId = StartWorkflow(new MyDataClass() { ActivityInput = new ActivityInput() { Value1 = "a", Value2 = 1 } }); + var workflowId = StartWorkflow(new MyDataClass { ActivityInput = new ActivityInput { Value1 = "a", Value2 = 1 } }); var activity = Host.GetPendingActivity("act-1", "worker1", TimeSpan.FromSeconds(30)).Result; if (activity != null) { var actInput = (ActivityInput)activity.Parameters; - Host.SubmitActivitySuccess(activity.Token, new ActivityOutput() + Host.SubmitActivitySuccess(activity.Token, new ActivityOutput { Value1 = actInput.Value1 + "1", Value2 = actInput.Value2 + 1 diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario.cs index 55c336020..a07d1e44a 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario.cs @@ -54,7 +54,7 @@ public CompensationScenario() [Fact] public void NoExceptionScenario() { - var workflowId = StartWorkflow(new MyDataClass() { ThrowException = false }); + var workflowId = StartWorkflow(new MyDataClass { ThrowException = false }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); @@ -68,7 +68,7 @@ public void NoExceptionScenario() [Fact] public void ExceptionScenario() { - var workflowId = StartWorkflow(new MyDataClass() { ThrowException = true }); + var workflowId = StartWorkflow(new MyDataClass { ThrowException = true }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs b/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs index 2b2000304..d27564f07 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs @@ -59,7 +59,7 @@ public CompensationScenario2() [Fact] public void NoExceptionScenario() { - var workflowId = StartWorkflow(new MyDataClass() { ThrowException = false }); + var workflowId = StartWorkflow(new MyDataClass { ThrowException = false }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); @@ -74,7 +74,7 @@ public void NoExceptionScenario() [Fact] public void ExceptionScenario() { - var workflowId = StartWorkflow(new MyDataClass() { ThrowException = true }); + var workflowId = StartWorkflow(new MyDataClass { ThrowException = true }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs index 0e460defd..d5adab795 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs @@ -62,7 +62,7 @@ public DataIOScenario() public void Scenario() { decimal v4 = 1.235465673450897890m; - var workflowId = StartWorkflow(new MyDataClass() {Value1 = 2, Value2 = 3, Value4 = v4, SubValue = new DataSubclass() {Value5 = v4}}); + var workflowId = StartWorkflow(new MyDataClass {Value1 = 2, Value2 = 3, Value4 = v4, SubValue = new DataSubclass {Value5 = v4}}); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DecisionScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DecisionScenario.cs index 3ae1faa66..4d9f733fd 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DecisionScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DecisionScenario.cs @@ -78,7 +78,7 @@ public DecisionScenario() [Fact] public void Scenario1() { - var workflowId = StartWorkflow(new MyDataClass() { Op = "+", Value1 = 2, Value2 = 3 }); + var workflowId = StartWorkflow(new MyDataClass { Op = "+", Value1 = 2, Value2 = 3 }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); @@ -89,7 +89,7 @@ public void Scenario1() [Fact] public void Scenario2() { - var workflowId = StartWorkflow(new MyDataClass() { Op = "-", Value1 = 2, Value2 = 3 }); + var workflowId = StartWorkflow(new MyDataClass { Op = "-", Value1 = 2, Value2 = 3 }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs index 416db55f2..aa6591f6a 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DynamicDataIOScenario.cs @@ -58,7 +58,7 @@ public DynamicDataIOScenario() [Fact] public void Scenario() { - var workflowId = StartWorkflow(new MyDataClass() { Value1 = 2, Value2 = 3 }); + var workflowId = StartWorkflow(new MyDataClass { Value1 = 2, Value2 = 3 }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/EventScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/EventScenario.cs index 86a6703df..7d5fd2dea 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/EventScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/EventScenario.cs @@ -42,7 +42,7 @@ public EventScenario() public void Scenario() { var eventKey = Guid.NewGuid().ToString(); - var workflowId = StartWorkflow(new MyDataClass() { StrValue1 = eventKey, StrValue2 = eventKey }); + var workflowId = StartWorkflow(new MyDataClass { StrValue1 = eventKey, StrValue2 = eventKey }); WaitForEventSubscription("MyEvent", eventKey, TimeSpan.FromSeconds(30)); Host.PublishEvent("MyEvent", eventKey, "Pass1"); WaitForEventSubscription("MyEvent2", eventKey, TimeSpan.FromSeconds(30)); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs index f10c8ee01..f16c89b24 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs @@ -44,7 +44,7 @@ public void Build(IWorkflowBuilder builder) Step1Ticker++; return ExecutionResult.Next(); }) - .ForEach(x => new List() { 2, 2, 3 }) + .ForEach(x => new List { 2, 2, 3 }) .Do(x => x.StartWith()) .Then(context => { diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ForeachSyncScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachSyncScenario.cs index 61cf3484c..74e29fd3a 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/ForeachSyncScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachSyncScenario.cs @@ -34,7 +34,7 @@ public void Build(IWorkflowBuilder builder) { builder .StartWith(_ => ExecutionResult.Next()) - .ForEach(x => new List() { 10, 2, 3 }, _ => false) + .ForEach(x => new List { 10, 2, 3 }, _ => false) .Do(x => x .StartWith() .Input(step => step.Period, (_, context) => TimeSpan.FromSeconds((int)context.Item)) diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/IfScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/IfScenario.cs index f7e0e7817..bd2c9808a 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/IfScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/IfScenario.cs @@ -78,7 +78,7 @@ public IfScenario() [Fact] public void Scenario() { - var workflowId = StartWorkflow(new MyDataClass() { Counter = 2 }); + var workflowId = StartWorkflow(new MyDataClass { Counter = 2 }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); Step1Ticker.Should().Be(1); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/SagaScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/SagaScenario.cs index fea1e4402..bfa935ef7 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/SagaScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/SagaScenario.cs @@ -75,7 +75,7 @@ public SagaScenario() [Fact] public void NoExceptionScenario() { - var workflowId = StartWorkflow(new MyDataClass() { ThrowException = false }); + var workflowId = StartWorkflow(new MyDataClass { ThrowException = false }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); @@ -95,7 +95,7 @@ public void NoExceptionScenario() [Fact] public void ExceptionScenario() { - var workflowId = StartWorkflow(new MyDataClass() { ThrowException = true }); + var workflowId = StartWorkflow(new MyDataClass { ThrowException = true }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs index 7d8a0117a..eb51107b8 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs @@ -21,7 +21,7 @@ public StoredJsonScenario() [Fact(DisplayName = "Execute branch 1")] public void should_execute_branch1() { - var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionJson(), new CounterBoard() { Flag1 = true, Flag2 = true, Flag3 = true }); + var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionJson(), new CounterBoard { Flag1 = true, Flag2 = true, Flag3 = true }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); var data = GetData(workflowId); @@ -40,7 +40,7 @@ public void should_execute_branch1() [Fact(DisplayName = "Execute branch 2")] public void should_execute_branch2() { - var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionJson(), new CounterBoard() { Flag1 = true, Flag2 = true, Flag3 = false }); + var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionJson(), new CounterBoard { Flag1 = true, Flag2 = true, Flag3 = false }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); var data = GetData(workflowId); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs index 3b37d2eb7..26b1f687a 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs @@ -21,7 +21,7 @@ public StoredYamlScenario() [Fact(DisplayName = "Execute workflow from stored YAML definition")] public void should_execute_yaml_workflow() { - var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionYaml(), new CounterBoard() { Flag1 = true, Flag2 = true }); + var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionYaml(), new CounterBoard { Flag1 = true, Flag2 = true }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); var data = GetData(workflowId); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/WhenScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/WhenScenario.cs index 95103dc63..7d7095e6c 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/WhenScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/WhenScenario.cs @@ -71,7 +71,7 @@ public WhenScenario() [Fact] public void Scenario() { - var workflowId = StartWorkflow(new MyDataClass() { Counter = 2 }); + var workflowId = StartWorkflow(new MyDataClass { Counter = 2 }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); Case1Ticker.Should().Be(0); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/WhileScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/WhileScenario.cs index 67d364ced..5ca2cd20a 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/WhileScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/WhileScenario.cs @@ -69,7 +69,7 @@ public WhileScenario() [Fact] public void Scenario() { - var workflowId = StartWorkflow(new MyDataClass() { Counter = 0 }); + var workflowId = StartWorkflow(new MyDataClass { Counter = 0 }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); Step1Ticker.Should().Be(1); diff --git a/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs b/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs index d244ff7c3..091dafe22 100644 --- a/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs +++ b/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs @@ -30,7 +30,7 @@ protected IEnumerable BuildTestData() { var result = new List(); - result.Add(new WorkflowInstance() + result.Add(new WorkflowInstance { Id = "1", CreateTime = new DateTime(2010, 1, 1), @@ -38,25 +38,25 @@ protected IEnumerable BuildTestData() Reference = "ref1" }); - result.Add(new WorkflowInstance() + result.Add(new WorkflowInstance { Id = "2", CreateTime = new DateTime(2020, 1, 1), Status = WorkflowStatus.Runnable, Reference = "ref2", - Data = new DataObject() + Data = new DataObject { Value3 = 7 } }); - result.Add(new WorkflowInstance() + result.Add(new WorkflowInstance { Id = "3", CreateTime = new DateTime(2010, 1, 1), Status = WorkflowStatus.Complete, Reference = "ref3", - Data = new DataObject() + Data = new DataObject { Value3 = 5, Value1 = "quick fox", @@ -64,13 +64,13 @@ protected IEnumerable BuildTestData() } }); - result.Add(new WorkflowInstance() + result.Add(new WorkflowInstance { Id = "4", CreateTime = new DateTime(2010, 1, 1), Status = WorkflowStatus.Complete, Reference = "ref4", - Data = new AltDataObject() + Data = new AltDataObject { Value1 = 9, Value2 = new DateTime(2000, 1, 1) @@ -165,7 +165,7 @@ class DataObject : ISearchable public IEnumerable GetSearchTokens() { - return new List() + return new List { Value1, Value2 diff --git a/test/WorkflowCore.TestAssets/Utils.cs b/test/WorkflowCore.TestAssets/Utils.cs index ea0f85f54..48a479b47 100644 --- a/test/WorkflowCore.TestAssets/Utils.cs +++ b/test/WorkflowCore.TestAssets/Utils.cs @@ -9,7 +9,7 @@ namespace WorkflowCore.TestAssets { public static class Utils { - private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All, DateFormatHandling = DateFormatHandling.IsoDateFormat, DateTimeZoneHandling = DateTimeZoneHandling.Utc }; + private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, DateFormatHandling = DateFormatHandling.IsoDateFormat, DateTimeZoneHandling = DateTimeZoneHandling.Utc }; public static T DeepCopy(T obj) { diff --git a/test/WorkflowCore.Testing/JsonWorkflowTest.cs b/test/WorkflowCore.Testing/JsonWorkflowTest.cs index 760966a52..cfabc7877 100644 --- a/test/WorkflowCore.Testing/JsonWorkflowTest.cs +++ b/test/WorkflowCore.Testing/JsonWorkflowTest.cs @@ -43,7 +43,7 @@ protected virtual void Setup() private void Host_OnStepError(WorkflowInstance workflow, WorkflowStep step, Exception exception) { - UnhandledStepErrors.Add(new StepError() + UnhandledStepErrors.Add(new StepError { Exception = exception, Step = step, diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/test/WorkflowCore.Testing/WorkflowTest.cs index 32a1259be..3f8a29ea3 100644 --- a/test/WorkflowCore.Testing/WorkflowTest.cs +++ b/test/WorkflowCore.Testing/WorkflowTest.cs @@ -48,7 +48,7 @@ protected virtual void Setup(bool registerClassMap = false) protected void Host_OnStepError(WorkflowInstance workflow, WorkflowStep step, Exception exception) { - UnhandledStepErrors.Add(new StepError() + UnhandledStepErrors.Add(new StepError { Exception = exception, Step = step, diff --git a/test/WorkflowCore.Testing/YamlWorkflowTest.cs b/test/WorkflowCore.Testing/YamlWorkflowTest.cs index b16d07a6d..a9d180b98 100644 --- a/test/WorkflowCore.Testing/YamlWorkflowTest.cs +++ b/test/WorkflowCore.Testing/YamlWorkflowTest.cs @@ -43,7 +43,7 @@ protected virtual void Setup() private void Host_OnStepError(WorkflowInstance workflow, WorkflowStep step, Exception exception) { - UnhandledStepErrors.Add(new StepError() + UnhandledStepErrors.Add(new StepError { Exception = exception, Step = step, diff --git a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs index 87b733a2d..0afa9bd7e 100644 --- a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs +++ b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs @@ -17,7 +17,7 @@ public abstract class BasePersistenceFixture [Fact] public void CreateNewWorkflow_should_generate_id() { - var workflow = new WorkflowInstance() + var workflow = new WorkflowInstance { Data = new { Value1 = 7 }, Description = "My Description", @@ -26,7 +26,7 @@ public void CreateNewWorkflow_should_generate_id() Version = 1, WorkflowDefinitionId = "My Workflow" }; - workflow.ExecutionPointers.Add(new ExecutionPointer() + workflow.ExecutionPointers.Add(new ExecutionPointer { Id = Guid.NewGuid().ToString(), Active = true, @@ -42,9 +42,9 @@ public void CreateNewWorkflow_should_generate_id() [Fact] public void GetWorkflowInstance_should_retrieve_workflow() { - var workflow = new WorkflowInstance() + var workflow = new WorkflowInstance { - Data = new TestData() { Value1 = 7 }, + Data = new TestData { Value1 = 7 }, Description = "My Description", Status = WorkflowStatus.Runnable, NextExecution = 0, @@ -52,13 +52,13 @@ public void GetWorkflowInstance_should_retrieve_workflow() WorkflowDefinitionId = "My Workflow", Reference = "My Reference" }; - workflow.ExecutionPointers.Add(new ExecutionPointer() + workflow.ExecutionPointers.Add(new ExecutionPointer { Id = "1", Active = true, StepId = 0, SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime(), - Scope = new List() { "4", "3", "2", "1" } + Scope = new List { "4", "3", "2", "1" } }); var workflowId = Subject.CreateNewWorkflow(workflow).Result; @@ -72,9 +72,9 @@ public void GetWorkflowInstance_should_retrieve_workflow() [Fact] public void GetWorkflowInstances_should_retrieve_workflows() { - var workflow01 = new WorkflowInstance() + var workflow01 = new WorkflowInstance { - Data = new TestData() { Value1 = 7 }, + Data = new TestData { Value1 = 7 }, Description = "My Description", Status = WorkflowStatus.Runnable, NextExecution = 0, @@ -82,19 +82,19 @@ public void GetWorkflowInstances_should_retrieve_workflows() WorkflowDefinitionId = "My Workflow", Reference = "My Reference" }; - workflow01.ExecutionPointers.Add(new ExecutionPointer() + workflow01.ExecutionPointers.Add(new ExecutionPointer { Id = "1", Active = true, StepId = 0, SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime(), - Scope = new List() { "4", "3", "2", "1" } + Scope = new List { "4", "3", "2", "1" } }); var workflowId01 = Subject.CreateNewWorkflow(workflow01).Result; - var workflow02 = new WorkflowInstance() + var workflow02 = new WorkflowInstance { - Data = new TestData() { Value1 = 7 }, + Data = new TestData { Value1 = 7 }, Description = "My Description", Status = WorkflowStatus.Runnable, NextExecution = 0, @@ -102,19 +102,19 @@ public void GetWorkflowInstances_should_retrieve_workflows() WorkflowDefinitionId = "My Workflow", Reference = "My Reference" }; - workflow02.ExecutionPointers.Add(new ExecutionPointer() + workflow02.ExecutionPointers.Add(new ExecutionPointer { Id = "1", Active = true, StepId = 0, SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime(), - Scope = new List() { "4", "3", "2", "1" } + Scope = new List { "4", "3", "2", "1" } }); var workflowId02 = Subject.CreateNewWorkflow(workflow02).Result; - var workflow03 = new WorkflowInstance() + var workflow03 = new WorkflowInstance { - Data = new TestData() { Value1 = 7 }, + Data = new TestData { Value1 = 7 }, Description = "My Description", Status = WorkflowStatus.Runnable, NextExecution = 0, @@ -122,13 +122,13 @@ public void GetWorkflowInstances_should_retrieve_workflows() WorkflowDefinitionId = "My Workflow", Reference = "My Reference" }; - workflow03.ExecutionPointers.Add(new ExecutionPointer() + workflow03.ExecutionPointers.Add(new ExecutionPointer { Id = "1", Active = true, StepId = 0, SleepUntil = new DateTime(2000, 1, 1).ToUniversalTime(), - Scope = new List() { "4", "3", "2", "1" } + Scope = new List { "4", "3", "2", "1" } }); var workflowId03 = Subject.CreateNewWorkflow(workflow03).Result; @@ -155,9 +155,9 @@ public void GetWorkflowInstances_should_retrieve_workflows() [Fact] public void PersistWorkflow() { - var oldWorkflow = new WorkflowInstance() + var oldWorkflow = new WorkflowInstance { - Data = new TestData() { Value1 = 7 }, + Data = new TestData { Value1 = 7 }, Description = "My Description", Status = WorkflowStatus.Runnable, NextExecution = 0, @@ -166,19 +166,19 @@ public void PersistWorkflow() CreateTime = new DateTime(2000, 1, 1).ToUniversalTime(), Reference = "My Reference" }; - oldWorkflow.ExecutionPointers.Add(new ExecutionPointer() + oldWorkflow.ExecutionPointers.Add(new ExecutionPointer { Id = Guid.NewGuid().ToString(), Active = true, StepId = 0, - Scope = new List() { "1", "2", "3", "4" } + Scope = new List { "1", "2", "3", "4" } }); var workflowId = Subject.CreateNewWorkflow(oldWorkflow).Result; var newWorkflow = Utils.DeepCopy(oldWorkflow); newWorkflow.Data = oldWorkflow.Data; newWorkflow.Reference = oldWorkflow.Reference; newWorkflow.NextExecution = 7; - newWorkflow.ExecutionPointers.Add(new ExecutionPointer() { Id = Guid.NewGuid().ToString(), Active = true, StepId = 1 }); + newWorkflow.ExecutionPointers.Add(new ExecutionPointer { Id = Guid.NewGuid().ToString(), Active = true, StepId = 1 }); Subject.PersistWorkflow(newWorkflow).Wait(); @@ -197,9 +197,9 @@ public void ConcurrentPersistWorkflow() { actions.Add(() => { - var oldWorkflow = new WorkflowInstance() + var oldWorkflow = new WorkflowInstance { - Data = new TestData() { Value1 = 7 }, + Data = new TestData { Value1 = 7 }, Description = "My Description", Status = WorkflowStatus.Runnable, NextExecution = 0, @@ -207,7 +207,7 @@ public void ConcurrentPersistWorkflow() WorkflowDefinitionId = "My Workflow", CreateTime = new DateTime(2000, 1, 1).ToUniversalTime() }; - oldWorkflow.ExecutionPointers.Add(new ExecutionPointer() + oldWorkflow.ExecutionPointers.Add(new ExecutionPointer { Id = Guid.NewGuid().ToString(), Active = true, @@ -216,7 +216,7 @@ public void ConcurrentPersistWorkflow() var workflowId = subject.CreateNewWorkflow(oldWorkflow).Result; var newWorkflow = Utils.DeepCopy(oldWorkflow); newWorkflow.NextExecution = 7; - newWorkflow.ExecutionPointers.Add(new ExecutionPointer() { Id = Guid.NewGuid().ToString(), Active = true, StepId = 1 }); + newWorkflow.ExecutionPointers.Add(new ExecutionPointer { Id = Guid.NewGuid().ToString(), Active = true, StepId = 1 }); subject.PersistWorkflow(newWorkflow).Wait(); // It will throw an exception if the persistence provider occurred resource competition. }); diff --git a/test/WorkflowCore.UnitTests/Models/MemberMapParameterTests.cs b/test/WorkflowCore.UnitTests/Models/MemberMapParameterTests.cs index c25b57857..b486d039e 100644 --- a/test/WorkflowCore.UnitTests/Models/MemberMapParameterTests.cs +++ b/test/WorkflowCore.UnitTests/Models/MemberMapParameterTests.cs @@ -18,7 +18,7 @@ public void should_assign_input() Expression> memberExpr = (x => x.Value1); Expression> valueExpr = (x => x.Value1); var subject = new MemberMapParameter(valueExpr, memberExpr); - var data = new MyData() + var data = new MyData { Value1 = 5 }; @@ -36,7 +36,7 @@ public void should_assign_output() Expression> valueExpr = (x => x.Value1); var subject = new MemberMapParameter(valueExpr, memberExpr); var data = new MyData(); - var step = new MyStep() + var step = new MyStep { Value1 = 5 }; @@ -53,7 +53,7 @@ public void should_convert_input() Expression> valueExpr = (x => x.Value1); var subject = new MemberMapParameter(valueExpr, memberExpr); - var data = new MyData() + var data = new MyData { Value1 = 5 }; @@ -72,7 +72,7 @@ public void should_convert_output() Expression> valueExpr = (x => x.Value1); var subject = new MemberMapParameter(valueExpr, memberExpr); - var data = new MyData() + var data = new MyData { Value1 = 5 }; diff --git a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs index 557abdb5a..f6bc0cf35 100644 --- a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs @@ -48,15 +48,15 @@ public void should_advance_workflow() { //arrange var definition = new WorkflowDefinition(); - var pointer1 = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; - var pointer2 = new ExecutionPointer() { Id = "2" }; - var outcome = new ValueOutcome() { NextStep = 1 }; + var pointer1 = new ExecutionPointer { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer2 = new ExecutionPointer { Id = "2" }; + var outcome = new ValueOutcome { NextStep = 1 }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer1); var result = ExecutionResult.Next(); - A.CallTo(() => step.Outcomes).Returns(new List() { outcome }); + A.CallTo(() => step.Outcomes).Returns(new List { outcome }); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome)).Returns(pointer2); //act @@ -77,7 +77,7 @@ public void should_set_persistence_data() //arrange var persistenceData = new object(); var definition = new WorkflowDefinition(); - var pointer = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer = new ExecutionPointer { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); @@ -95,7 +95,7 @@ public void should_subscribe_to_event() { //arrange var definition = new WorkflowDefinition(); - var pointer = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer = new ExecutionPointer { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); @@ -117,19 +117,19 @@ public void should_select_correct_outcomes() { //arrange var definition = new WorkflowDefinition(); - var pointer1 = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; - var pointer2 = new ExecutionPointer() { Id = "2" }; - var pointer3 = new ExecutionPointer() { Id = "3" }; + var pointer1 = new ExecutionPointer { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer2 = new ExecutionPointer { Id = "2" }; + var pointer3 = new ExecutionPointer { Id = "3" }; Expression> expr1 = data => 10; Expression> expr2 = data => 20; - var outcome1 = new ValueOutcome() { NextStep = 1, Value = expr1 }; - var outcome2 = new ValueOutcome() { NextStep = 2, Value = expr2 }; + var outcome1 = new ValueOutcome { NextStep = 1, Value = expr1 }; + var outcome2 = new ValueOutcome { NextStep = 2, Value = expr2 }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer1); var result = ExecutionResult.Outcome(20); - A.CallTo(() => step.Outcomes).Returns(new List() { outcome1, outcome2 }); + A.CallTo(() => step.Outcomes).Returns(new List { outcome1, outcome2 }); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome1)).Returns(pointer2); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome2)).Returns(pointer3); @@ -153,7 +153,7 @@ public void should_sleep_pointer() //arrange var persistenceData = new object(); var definition = new WorkflowDefinition(); - var pointer = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; + var pointer = new ExecutionPointer { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); @@ -174,14 +174,14 @@ public void should_branch_children() var branch = 10; var child = 2; var definition = new WorkflowDefinition(); - var pointer = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; - var childPointer = new ExecutionPointer() { Id = "2" }; + var pointer = new ExecutionPointer { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; + var childPointer = new ExecutionPointer { Id = "2" }; var step = A.Fake(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); - var result = ExecutionResult.Branch(new List() { branch }, null); + var result = ExecutionResult.Branch(new List { branch }, null); - A.CallTo(() => step.Children).Returns(new List() { child }); + A.CallTo(() => step.Children).Returns(new List { child }); A.CallTo(() => PointerFactory.BuildChildPointer(definition, pointer, child, branch)).Returns(childPointer); //act @@ -197,7 +197,7 @@ private static WorkflowInstance GivenWorkflow(ExecutionPointer pointer) return new WorkflowInstance { Status = WorkflowStatus.Runnable, - ExecutionPointers = new ExecutionPointerCollection(new List() + ExecutionPointers = new ExecutionPointerCollection(new List { pointer }) diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index cefaf9a23..037dd0e33 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -91,9 +91,9 @@ public void should_execute_active_step() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new ExecutionPointerCollection(new List() + ExecutionPointers = new ExecutionPointerCollection(new List { - new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + new ExecutionPointer { Id = "1", Active = true, StepId = 0 } }) }; @@ -121,9 +121,9 @@ public void should_trigger_step_hooks() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new ExecutionPointerCollection(new List() + ExecutionPointers = new ExecutionPointerCollection(new List { - new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + new ExecutionPointer { Id = "1", Active = true, StepId = 0 } }) }; @@ -152,9 +152,9 @@ public void should_not_execute_inactive_step() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new ExecutionPointerCollection(new List() + ExecutionPointers = new ExecutionPointerCollection(new List { - new ExecutionPointer() { Id = "1", Active = false, StepId = 0 } + new ExecutionPointer { Id = "1", Active = false, StepId = 0 } }) }; @@ -173,7 +173,7 @@ public void should_map_inputs() var step1Body = A.Fake(); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); - WorkflowStep step1 = BuildFakeStep(step1Body, new List() + WorkflowStep step1 = BuildFakeStep(step1Body, new List { param } @@ -188,10 +188,10 @@ public void should_map_inputs() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - Data = new DataClass() { Value1 = 5 }, - ExecutionPointers = new ExecutionPointerCollection(new List() + Data = new DataClass { Value1 = 5 }, + ExecutionPointers = new ExecutionPointerCollection(new List { - new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + new ExecutionPointer { Id = "1", Active = true, StepId = 0 } }) }; @@ -212,7 +212,7 @@ public void should_map_outputs() var step1Body = A.Fake(); A.CallTo(() => step1Body.Property1).Returns(7); A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); - WorkflowStep step1 = BuildFakeStep(step1Body, new List(), new List() + WorkflowStep step1 = BuildFakeStep(step1Body, new List(), new List { param } @@ -220,7 +220,7 @@ public void should_map_outputs() Given1StepWorkflow(step1, "Workflow", 1); - var data = new DataClass() { Value1 = 5 }; + var data = new DataClass { Value1 = 5 }; var instance = new WorkflowInstance { @@ -230,9 +230,9 @@ public void should_map_outputs() NextExecution = 0, Id = "001", Data = data, - ExecutionPointers = new ExecutionPointerCollection(new List() + ExecutionPointers = new ExecutionPointerCollection(new List { - new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + new ExecutionPointer { Id = "1", Active = true, StepId = 0 } }) }; @@ -262,9 +262,9 @@ public void should_handle_step_exception() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new ExecutionPointerCollection(new List() + ExecutionPointers = new ExecutionPointerCollection(new List { - new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + new ExecutionPointer { Id = "1", Active = true, StepId = 0 } }) }; @@ -293,9 +293,9 @@ public void should_process_after_execution_iteration() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new ExecutionPointerCollection(new List() + ExecutionPointers = new ExecutionPointerCollection(new List { - new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + new ExecutionPointer { Id = "1", Active = true, StepId = 0 } }) }; @@ -322,9 +322,9 @@ public void should_process_cancellations() Status = WorkflowStatus.Runnable, NextExecution = 0, Id = "001", - ExecutionPointers = new ExecutionPointerCollection(new List() + ExecutionPointers = new ExecutionPointerCollection(new List { - new ExecutionPointer() { Id = "1", Active = true, StepId = 0 } + new ExecutionPointer { Id = "1", Active = true, StepId = 0 } }) }; @@ -338,12 +338,12 @@ public void should_process_cancellations() private void Given1StepWorkflow(WorkflowStep step1, string id, int version) { - A.CallTo(() => Registry.GetDefinition(id, version)).Returns(new WorkflowDefinition() + A.CallTo(() => Registry.GetDefinition(id, version)).Returns(new WorkflowDefinition { Id = id, Version = version, DataType = typeof(object), - Steps = new WorkflowStepCollection() + Steps = new WorkflowStepCollection { step1 } From 6e53ad9a404b3d5ab3cf9bceaa0e21280acaa591 Mon Sep 17 00:00:00 2001 From: macrian Date: Thu, 11 Mar 2021 10:36:18 +0200 Subject: [PATCH 302/462] Cleanup using statements --- src/WorkflowCore.DSL/Models/DefinitionSource.cs | 2 -- src/WorkflowCore.DSL/Models/Envelope.cs | 2 -- src/WorkflowCore.DSL/Models/v1/MappingSourceV1.cs | 2 -- src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs | 1 - src/WorkflowCore.DSL/ServiceCollectionExtensions.cs | 3 --- src/WorkflowCore.DSL/Services/DefinitionLoader.cs | 5 +---- .../Exceptions/WorkflowDefinitionLoadException.cs | 2 -- src/WorkflowCore/Interface/IActivityController.cs | 2 -- src/WorkflowCore/Interface/ICancellationProcessor.cs | 2 -- src/WorkflowCore/Interface/ILifeCycleEventHub.cs | 2 -- .../Interface/ILifeCycleEventPublisher.cs | 2 -- src/WorkflowCore/Interface/ISearchIndex.cs | 2 -- src/WorkflowCore/Interface/ISearchable.cs | 1 - src/WorkflowCore/Interface/IStepBuilder.cs | 2 -- src/WorkflowCore/Interface/IStepParameter.cs | 5 +---- src/WorkflowCore/Interface/IWorkflowController.cs | 2 -- src/WorkflowCore/Interface/IWorkflowErrorHandler.cs | 1 - src/WorkflowCore/Models/ActionParameter.cs | 2 -- src/WorkflowCore/Models/ActivityResult.cs | 2 -- src/WorkflowCore/Models/ExecutionPointerCollection.cs | 1 - .../Models/LifeCycleEvents/LifeCycleEvent.cs | 2 -- .../Models/LifeCycleEvents/StepCompleted.cs | 2 -- .../Models/LifeCycleEvents/StepStarted.cs | 2 -- .../Models/LifeCycleEvents/WorkflowCompleted.cs | 2 -- .../Models/LifeCycleEvents/WorkflowError.cs | 2 -- .../Models/LifeCycleEvents/WorkflowResumed.cs | 2 -- .../Models/LifeCycleEvents/WorkflowStarted.cs | 2 -- .../Models/LifeCycleEvents/WorkflowSuspended.cs | 2 -- .../Models/LifeCycleEvents/WorkflowTerminated.cs | 2 -- src/WorkflowCore/Models/MemberMapParameter.cs | 1 - src/WorkflowCore/Models/Search/Page.cs | 1 - src/WorkflowCore/Models/Search/SearchFilter.cs | 2 -- src/WorkflowCore/Models/Search/StepInfo.cs | 2 -- .../Models/Search/WorkflowSearchResult.cs | 2 -- src/WorkflowCore/Models/WorkflowDefinition.cs | 1 - src/WorkflowCore/Models/WorkflowInstance.cs | 1 - src/WorkflowCore/Models/WorkflowStep.cs | 1 - src/WorkflowCore/Models/WorkflowStepCollection.cs | 1 - src/WorkflowCore/Primitives/ContainerStepBody.cs | 1 - src/WorkflowCore/Primitives/SagaContainer.cs | 4 +--- src/WorkflowCore/Properties/AssemblyInfo.cs | 1 - src/WorkflowCore/ServiceCollectionExtensions.cs | 4 ---- src/WorkflowCore/Services/ActivityController.cs | 1 - .../Services/BackgroundTasks/WorkflowConsumer.cs | 1 - src/WorkflowCore/Services/CancellationProcessor.cs | 2 -- .../DefaultProviders/MemoryPersistenceProvider.cs | 1 - .../Services/DefaultProviders/NullSearchIndex.cs | 2 -- .../Services/DefaultProviders/SingleNodeEventHub.cs | 1 - .../TransientMemoryPersistenceProvider.cs | 1 - .../Services/ErrorHandlers/CompensateHandler.cs | 2 -- .../Services/ErrorHandlers/RetryHandler.cs | 2 -- .../Services/ErrorHandlers/SuspendHandler.cs | 1 - .../Services/ErrorHandlers/TerminateHandler.cs | 1 - src/WorkflowCore/Services/ExecutionPointerFactory.cs | 1 - src/WorkflowCore/Services/LifeCycleEventPublisher.cs | 2 -- src/WorkflowCore/Services/SyncWorkflowRunner.cs | 2 -- src/WorkflowCore/Services/WorkflowController.cs | 1 - src/WorkflowCore/Services/WorkflowHost.cs | 3 --- .../WorkflowCore.Users/Interface/IUserTaskBuilder.cs | 4 ---- .../Interface/IUserTaskReturnBuilder.cs | 2 -- .../WorkflowCore.Users/Models/OpenUserAction.cs | 1 - .../WorkflowCore.Users/Models/UserAction.cs | 2 -- src/extensions/WorkflowCore.Users/Models/UserStep.cs | 2 -- .../WorkflowCore.Users/Models/UserStepContainer.cs | 1 - .../WorkflowCore.Users/Primitives/Escalate.cs | 2 -- .../WorkflowCore.Users/Primitives/EscalateStep.cs | 4 ---- .../WorkflowCore.Users/Primitives/UserTaskStep.cs | 2 -- .../WorkflowCore.Users/Properties/AssemblyInfo.cs | 1 - .../ServiceExtensions/StepBuilderExtensions.cs | 4 ---- .../ServiceExtensions/WorkflowHostExtensions.cs | 3 --- .../WorkflowCore.Users/Services/UserTaskBuilder.cs | 2 -- .../Services/UserTaskReturnBuilder.cs | 2 -- .../Controllers/EventsController.cs | 1 - .../Controllers/WorkflowsController.cs | 1 - .../WorkflowCore.WebAPI/Properties/AssemblyInfo.cs | 1 - .../ServiceCollectionExtensions.cs | 2 -- .../ServiceCollectionExtensions.cs | 2 -- .../SqlLockProvider.cs | 1 - .../ExtensionMethods.cs | 1 - .../Interfaces/IWorkflowDbContextFactory.cs | 2 -- .../Models/PersistedEvent.cs | 2 -- .../Models/PersistedExecutionError.cs | 4 ---- .../Models/PersistedExecutionPointer.cs | 1 - .../Models/PersistedExecutionPointerCollection.cs | 1 - .../Models/PersistedExtensionAttribute.cs | 3 --- .../Models/PersistedSubscription.cs | 3 --- .../Models/PersistedWorkflow.cs | 3 --- .../Properties/AssemblyInfo.cs | 1 - .../Services/EntityFrameworkPersistenceProvider.cs | 4 ---- .../Services/WorkflowDbContext.cs | 6 ------ .../WorkflowCore.Persistence.MongoDB/ConfigOptions.cs | 2 -- .../Properties/AssemblyInfo.cs | 1 - .../ServiceCollectionExtensions.cs | 2 -- .../AssemblyQualifiedDiscriminatorConvention.cs | 2 -- .../MysqlPersistenceProviderModelSnapshot.cs | 2 -- .../Migrations/20170126230815_InitialDatabase.cs | 1 - .../Migrations/20170312161610_Events.cs | 1 - .../Migrations/20170507214430_ControlStructures.cs | 1 - .../Migrations/20170519231452_PersistOutcome.cs | 1 - .../Migrations/20170722200412_WfReference.cs | 1 - .../Migrations/20171223020844_StepScope.cs | 1 - .../PostgresPersistenceProviderModelSnapshot.cs | 2 -- .../PostgresContext.cs | 2 -- .../PostgresContextFactory.cs | 2 -- .../Properties/AssemblyInfo.cs | 1 - .../ServiceCollectionExtensions.cs | 2 -- .../RavenStoreOptions.cs | 2 -- .../ServiceCollectionExtensions.cs | 6 +----- .../Services/RavenDbIndexes.cs | 2 -- .../Services/RavendbPersistenceProvider.cs | 1 - .../Services/WorkflowPurger.cs | 4 ---- .../Migrations/20170126230839_InitialDatabase.cs | 1 - .../Migrations/20170312161610_Events.cs | 1 - .../Migrations/20170507214430_ControlStructures.cs | 1 - .../Migrations/20170519231452_PersistOutcome.cs | 1 - .../Migrations/20170722195832_WfReference.cs | 1 - .../Migrations/20171223020645_StepScope.cs | 1 - .../SqlServerPersistenceProviderModelSnapshot.cs | 3 --- .../Properties/AssemblyInfo.cs | 1 - .../ServiceCollectionExtensions.cs | 1 - .../SqlContextFactory.cs | 1 - .../SqlServerContext.cs | 2 -- .../Properties/AssemblyInfo.cs | 1 - .../ServiceCollectionExtensions.cs | 2 -- .../WorkflowCore.Persistence.Sqlite/SqliteContext.cs | 2 -- .../SqliteContextFactory.cs | 2 -- .../Interface/IKinesisStreamConsumer.cs | 2 -- .../Interface/IKinesisTracker.cs | 2 -- .../WorkflowCore.Providers.AWS/ModelExtensions.cs | 3 --- .../Services/DynamoDbProvisioner.cs | 1 - .../Services/DynamoPersistenceProvider.cs | 1 - .../Services/KinesisProvider.cs | 1 - .../Services/KinesisTracker.cs | 2 -- .../Models/ControlledLock.cs | 2 -- .../Models/PersistedEvent.cs | 1 - .../Models/PersistedSubscription.cs | 1 - .../Models/PersistedWorkflow.cs | 1 - .../Services/AzureLockManager.cs | 1 - .../Services/AzureStorageQueueProvider.cs | 1 - .../Services/CosmosDbPersistenceProvider.cs | 1 - .../Services/CosmosDbProvisioner.cs | 1 - .../Services/ElasticsearchIndexer.cs | 1 - .../ServiceCollectionExtensions.cs | 1 - .../Services/RedisLifeCycleEventHub.cs | 1 - .../Services/RedisLockProvider.cs | 1 - .../Services/RedisPersistenceProvider.cs | 1 - .../Services/RedisQueueProvider.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Services/RabbitMQProvider.cs | 3 --- .../Interfaces/ISqlServerQueueProviderMigrator.cs | 2 -- .../Models/QueueConfig.cs | 2 -- .../ServiceCollectionExtensions.cs | 2 -- .../Services/QueueConfigProvider.cs | 1 - .../Services/SqlCommandExecutor.cs | 2 -- .../Services/SqlServerQueueProviderMigrator.cs | 1 - .../WorkflowCore.Sample01/HelloWorldWorkflow.cs | 3 --- src/samples/WorkflowCore.Sample01/Program.cs | 4 ---- .../WorkflowCore.Sample01/Properties/AssemblyInfo.cs | 1 - .../WorkflowCore.Sample01/Steps/GoodbyeWorld.cs | 2 -- src/samples/WorkflowCore.Sample01/Steps/HelloWorld.cs | 2 -- src/samples/WorkflowCore.Sample02/Program.cs | 4 ---- .../WorkflowCore.Sample02/Properties/AssemblyInfo.cs | 1 - .../WorkflowCore.Sample02/SimpleDecisionWorkflow.cs | 2 -- .../WorkflowCore.Sample02/Steps/CustomMessage.cs | 2 -- .../WorkflowCore.Sample02/Steps/GoodbyeWorld.cs | 2 -- src/samples/WorkflowCore.Sample02/Steps/HelloWorld.cs | 2 -- .../WorkflowCore.Sample02/Steps/RandomOutput.cs | 2 -- src/samples/WorkflowCore.Sample03/MyDataClass.cs | 4 ---- .../WorkflowCore.Sample03/PassingDataWorkflow.cs | 2 -- src/samples/WorkflowCore.Sample03/Program.cs | 4 ---- .../WorkflowCore.Sample03/Properties/AssemblyInfo.cs | 1 - src/samples/WorkflowCore.Sample03/Steps/AddNumbers.cs | 1 - .../WorkflowCore.Sample03/Steps/CustomMessage.cs | 2 -- .../WorkflowCore.Sample03/Steps/GoodbyeWorld.cs | 2 -- src/samples/WorkflowCore.Sample03/Steps/HelloWorld.cs | 2 -- .../WorkflowCore.Sample04/EventSampleWorkflow.cs | 2 -- src/samples/WorkflowCore.Sample04/MyDataClass.cs | 2 -- src/samples/WorkflowCore.Sample04/Program.cs | 10 ---------- .../WorkflowCore.Sample04/Properties/AssemblyInfo.cs | 1 - .../WorkflowCore.Sample04/Steps/CustomMessage.cs | 2 -- .../WorkflowCore.Sample05/DeferSampleWorkflow.cs | 2 -- src/samples/WorkflowCore.Sample05/Program.cs | 6 ------ .../WorkflowCore.Sample05/Properties/AssemblyInfo.cs | 1 - src/samples/WorkflowCore.Sample05/Steps/SleepStep.cs | 2 -- .../WorkflowCore.Sample06/MultipleOutcomeWorkflow.cs | 2 -- src/samples/WorkflowCore.Sample06/Program.cs | 6 ------ .../WorkflowCore.Sample06/Properties/AssemblyInfo.cs | 1 - .../WorkflowCore.Sample06/Steps/RandomOutput.cs | 2 -- src/samples/WorkflowCore.Sample06/Steps/TaskA.cs | 2 -- src/samples/WorkflowCore.Sample06/Steps/TaskB.cs | 2 -- src/samples/WorkflowCore.Sample06/Steps/TaskC.cs | 2 -- src/samples/WorkflowCore.Sample06/Steps/TaskD.cs | 2 -- src/samples/WorkflowCore.Sample07/Program.cs | 2 -- src/samples/WorkflowCore.Sample07/Startup.cs | 3 --- src/samples/WorkflowCore.Sample08/HumanWorkflow.cs | 3 --- src/samples/WorkflowCore.Sample08/Program.cs | 3 --- .../WorkflowCore.Sample08/Properties/AssemblyInfo.cs | 1 - src/samples/WorkflowCore.Sample09/ForEachWorkflow.cs | 2 -- src/samples/WorkflowCore.Sample09/Program.cs | 1 - .../WorkflowCore.Sample09/Steps/DisplayContext.cs | 2 -- .../WorkflowCore.Sample09/Steps/DoSomething.cs | 2 -- src/samples/WorkflowCore.Sample09/Steps/SayGoodbye.cs | 2 -- src/samples/WorkflowCore.Sample09/Steps/SayHello.cs | 2 -- .../WorkflowCore.Sample09s/ForEachSyncWorkflow.cs | 2 -- src/samples/WorkflowCore.Sample09s/Program.cs | 1 - .../WorkflowCore.Sample09s/Steps/DisplayContext.cs | 2 -- .../WorkflowCore.Sample09s/Steps/DoSomething.cs | 2 -- .../WorkflowCore.Sample09s/Steps/SayGoodbye.cs | 2 -- src/samples/WorkflowCore.Sample09s/Steps/SayHello.cs | 2 -- src/samples/WorkflowCore.Sample10/Program.cs | 1 - .../WorkflowCore.Sample10/Steps/DoSomething.cs | 2 -- .../WorkflowCore.Sample10/Steps/IncrementStep.cs | 2 -- src/samples/WorkflowCore.Sample10/Steps/SayGoodbye.cs | 2 -- src/samples/WorkflowCore.Sample10/Steps/SayHello.cs | 2 -- src/samples/WorkflowCore.Sample10/WhileWorkflow.cs | 3 --- src/samples/WorkflowCore.Sample11/IfWorkflow.cs | 3 --- src/samples/WorkflowCore.Sample11/Program.cs | 1 - .../WorkflowCore.Sample11/Steps/PrintMessage.cs | 2 -- src/samples/WorkflowCore.Sample11/Steps/SayGoodbye.cs | 2 -- src/samples/WorkflowCore.Sample11/Steps/SayHello.cs | 2 -- src/samples/WorkflowCore.Sample12/OutcomeWorkflow.cs | 3 --- src/samples/WorkflowCore.Sample12/Program.cs | 1 - .../WorkflowCore.Sample12/Steps/DetermineSomething.cs | 3 --- .../WorkflowCore.Sample12/Steps/PrintMessage.cs | 2 -- src/samples/WorkflowCore.Sample12/Steps/SayGoodbye.cs | 2 -- src/samples/WorkflowCore.Sample12/Steps/SayHello.cs | 2 -- src/samples/WorkflowCore.Sample13/ParallelWorkflow.cs | 3 --- src/samples/WorkflowCore.Sample13/Program.cs | 5 ----- .../WorkflowCore.Sample13/Steps/PrintMessage.cs | 2 -- src/samples/WorkflowCore.Sample13/Steps/SayGoodbye.cs | 2 -- src/samples/WorkflowCore.Sample13/Steps/SayHello.cs | 2 -- src/samples/WorkflowCore.Sample14/Program.cs | 1 - .../WorkflowCore.Sample14/RecurSampleWorkflow.cs | 3 --- .../WorkflowCore.Sample15/HelloWorldWorkflow.cs | 3 --- src/samples/WorkflowCore.Sample15/Program.cs | 1 - .../WorkflowCore.Sample15/Services/MyService.cs | 2 -- .../WorkflowCore.Sample15/Steps/DoSomething.cs | 5 +---- src/samples/WorkflowCore.Sample15/Steps/HelloWorld.cs | 2 -- src/samples/WorkflowCore.Sample16/Program.cs | 1 - src/samples/WorkflowCore.Sample16/ScheduleWorkflow.cs | 2 -- .../WorkflowCore.Sample17/CompensatingWorkflow.cs | 3 --- src/samples/WorkflowCore.Sample18/ActivityWorkflow.cs | 3 --- src/samples/WorkflowCore.Sample18/Program.cs | 2 -- .../WorkflowCore.Sample18/Steps/CustomMessage.cs | 2 -- .../WorkflowCore.Sample18/Steps/GoodbyeWorld.cs | 2 -- src/samples/WorkflowCore.Sample18/Steps/HelloWorld.cs | 2 -- src/samples/WorkflowCore.Sample19/Program.cs | 1 - src/samples/WorkflowCore.TestSample01/NUnitTest.cs | 2 -- .../WorkflowCore.TestSample01/Workflow/AddNumbers.cs | 2 -- .../WorkflowCore.TestSample01/Workflow/MyDataClass.cs | 2 -- .../WorkflowCore.TestSample01/Workflow/MyWorkflow.cs | 2 -- test/Docker.Testify/DockerSetup.cs | 3 --- test/Docker.Testify/PortsInUseException.cs | 2 -- test/ScratchPad/ElasticTest.cs | 11 +---------- test/ScratchPad/Program.cs | 5 +---- .../Properties/AssemblyInfo.cs | 1 - .../Scenarios/ActivityScenario.cs | 2 -- .../Scenarios/AttachScenario.cs | 3 --- .../Scenarios/BaseScenario.cs | 2 -- .../Scenarios/BasicScenario.cs | 2 -- .../Scenarios/CancelledEventScenario.cs | 2 -- .../Scenarios/CompensationScenario.cs | 2 -- .../Scenarios/CompensationScenario2.cs | 2 -- .../Scenarios/DataIOScenario.cs | 2 -- .../Scenarios/DiScenario.cs | 1 - .../Scenarios/EndStepScenario.cs | 2 -- .../Scenarios/EventOrderScenario.cs | 2 -- .../Scenarios/EventScenario.cs | 2 -- .../Scenarios/FailingSagaScenario.cs | 2 -- .../Scenarios/ForeachScenario.cs | 1 - .../Scenarios/ForkScenario.cs | 2 -- .../Scenarios/IfScenario.cs | 2 -- .../Scenarios/MultistepCompensationScenario.cs | 2 -- .../Scenarios/NestedRetrySagaScenario.cs | 2 -- .../Scenarios/ParallelScenario.cs | 2 -- .../Scenarios/RetrySagaScenario.cs | 2 -- .../Scenarios/SagaScenario.cs | 2 -- .../Scenarios/StoredJsonScenario.cs | 4 ---- .../Scenarios/StoredYamlScenario.cs | 4 ---- .../Scenarios/UserScenario.cs | 4 ---- .../Scenarios/WhenScenario.cs | 2 -- .../Scenarios/WhileScenario.cs | 2 -- .../WorkflowCore.IntegrationTests/SearchIndexTests.cs | 3 --- .../WorkflowCore.TestAssets/DataTypes/CounterBoard.cs | 2 -- .../LockProvider/DistributedLockProviderTests.cs | 2 -- .../Properties/AssemblyInfo.cs | 1 - test/WorkflowCore.TestAssets/Steps/Counter.cs | 2 -- test/WorkflowCore.TestAssets/Utils.cs | 2 -- test/WorkflowCore.Testing/JsonWorkflowTest.cs | 2 -- test/WorkflowCore.Testing/WorkflowTest.cs | 2 -- test/WorkflowCore.Testing/YamlWorkflowTest.cs | 2 -- .../DynamoDbDockerSetup.cs | 7 +------ .../DynamoPersistenceProviderFixture.cs | 2 -- .../Scenarios/DynamoBasicScenario.cs | 4 ---- .../Scenarios/DynamoCompensationScenario.cs | 4 ---- .../Scenarios/DynamoDataScenario.cs | 4 ---- .../Scenarios/DynamoDynamicDataScenario.cs | 4 ---- .../Scenarios/DynamoEventScenario.cs | 4 ---- .../Scenarios/DynamoForeachScenario.cs | 4 ---- .../Scenarios/DynamoIfScenario.cs | 4 ---- .../Scenarios/DynamoRetrySagaScenario.cs | 4 ---- .../Scenarios/DynamoSagaScenario.cs | 4 ---- .../Scenarios/DynamoWhileScenario.cs | 4 ---- .../ElasticsearchDockerSetup.cs | 2 -- test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs | 7 +------ .../MongoPersistenceProviderFixture.cs | 2 -- .../Properties/AssemblyInfo.cs | 1 - .../Scenarios/MongoActivityScenario.cs | 2 -- .../Scenarios/MongoBasicScenario.cs | 2 -- .../Scenarios/MongoCompensationScenario.cs | 2 -- .../Scenarios/MongoDataScenario.cs | 2 -- .../Scenarios/MongoDecisionScenario.cs | 3 --- .../Scenarios/MongoEventScenario.cs | 2 -- .../Scenarios/MongoForEachScenario.cs | 2 -- .../Scenarios/MongoForkScenario.cs | 2 -- .../Scenarios/MongoIfScenario.cs | 2 -- .../Scenarios/MongoRetrySagaScenario.cs | 2 -- .../Scenarios/MongoSagaScenario.cs | 2 -- .../Scenarios/MongoUserScenario.cs | 2 -- .../Scenarios/MongoWhenScenario.cs | 2 -- .../Scenarios/MongoWhileScenario.cs | 2 -- .../Scenarios/MysqlDynamicDataScenario.cs | 1 - .../Scenarios/MysqlEventScenario.cs | 3 --- .../Scenarios/MysqlForeachScenario.cs | 3 --- .../Scenarios/MysqlForkScenario.cs | 3 --- .../Scenarios/MysqlIfScenario.cs | 3 --- .../Scenarios/MysqlRetrySagaScenario.cs | 3 --- .../Scenarios/MysqlSagaScenario.cs | 3 --- .../Scenarios/MysqlUserScenario.cs | 3 --- .../Scenarios/MysqlWhenScenario.cs | 3 --- .../Scenarios/MysqlWhileScenario.cs | 3 --- test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs | 7 +------ .../PostgresPersistenceProviderFixture.cs | 2 -- .../Properties/AssemblyInfo.cs | 1 - .../Scenarios/PostgresActivityScenario.cs | 2 -- .../Scenarios/PostgresBasicScenario.cs | 2 -- .../Scenarios/PostgresDataScenario.cs | 2 -- .../Scenarios/PostgresEventScenario.cs | 2 -- .../Scenarios/PostgresForeachScenario.cs | 2 -- .../Scenarios/PostgresForkScenario.cs | 2 -- .../Scenarios/PostgresIfScenario.cs | 2 -- .../Scenarios/PostgresRetrySagaScenario.cs | 2 -- .../Scenarios/PostgresSagaScenario.cs | 2 -- .../Scenarios/PostgresUserScenario.cs | 2 -- .../Scenarios/PostgresWhenScenario.cs | 2 -- .../Scenarios/PostgresWhileScenario.cs | 2 -- test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs | 6 +----- .../RedisPersistenceProviderFixture.cs | 2 -- test/WorkflowCore.Tests.SqlServer/DockerSetup.cs | 7 +------ .../Scenarios/SqlServerActivityScenario.cs | 2 -- .../Scenarios/SqlServerBasicScenario.cs | 2 -- .../Scenarios/SqlServerCompenstationScenario.cs | 2 -- .../Scenarios/SqlServerDataScenario.cs | 2 -- .../Scenarios/SqlServerEventScenario.cs | 2 -- .../Scenarios/SqlServerForEachScenario.cs | 2 -- .../Scenarios/SqlServerIfScenario.cs | 2 -- .../Scenarios/SqlServerRetrySagaScenario.cs | 2 -- .../Scenarios/SqlServerSagaScenario.cs | 2 -- .../Scenarios/SqlServerWhenScenario.cs | 2 -- .../Scenarios/SqlServerWhileScenario.cs | 2 -- test/WorkflowCore.Tests.Sqlite/SqliteCollection.cs | 2 -- .../SqlitePersistenceProviderFixture.cs | 2 -- .../Models/MemberMapParameterTests.cs | 1 - .../WorkflowCore.UnitTests/Properties/AssemblyInfo.cs | 1 - .../DefinitionStorage/DefinitionLoaderTests.cs | 4 ---- .../Services/ExecutionResultProcessorFixture.cs | 3 --- .../Services/MemoryPersistenceProviderFixture.cs | 2 -- .../Services/WorkflowRegistryFixture.cs | 8 -------- .../SingleNodeLockProviderTests.cs | 5 +---- 369 files changed, 13 insertions(+), 798 deletions(-) diff --git a/src/WorkflowCore.DSL/Models/DefinitionSource.cs b/src/WorkflowCore.DSL/Models/DefinitionSource.cs index 5d2bf0333..ec23f81c4 100644 --- a/src/WorkflowCore.DSL/Models/DefinitionSource.cs +++ b/src/WorkflowCore.DSL/Models/DefinitionSource.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.DefinitionStorage { diff --git a/src/WorkflowCore.DSL/Models/Envelope.cs b/src/WorkflowCore.DSL/Models/Envelope.cs index 77448f446..7258a6d5d 100644 --- a/src/WorkflowCore.DSL/Models/Envelope.cs +++ b/src/WorkflowCore.DSL/Models/Envelope.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.DefinitionStorage { diff --git a/src/WorkflowCore.DSL/Models/v1/MappingSourceV1.cs b/src/WorkflowCore.DSL/Models/v1/MappingSourceV1.cs index 94c21e3be..d4d920544 100644 --- a/src/WorkflowCore.DSL/Models/v1/MappingSourceV1.cs +++ b/src/WorkflowCore.DSL/Models/v1/MappingSourceV1.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.DefinitionStorage.v1 { diff --git a/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs b/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs index 82969d765..fb67ba449 100644 --- a/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs +++ b/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Dynamic; -using System.Text; namespace WorkflowCore.Models.DefinitionStorage.v1 { diff --git a/src/WorkflowCore.DSL/ServiceCollectionExtensions.cs b/src/WorkflowCore.DSL/ServiceCollectionExtensions.cs index 113236e91..5c4944f4f 100644 --- a/src/WorkflowCore.DSL/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore.DSL/ServiceCollectionExtensions.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Services.DefinitionStorage; diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index d408bfc07..b057b223a 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -1,17 +1,14 @@ -using Newtonsoft.Json; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; using System.Linq.Expressions; using System.Reflection; -using System.Text; using Newtonsoft.Json.Linq; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Primitives; -using WorkflowCore.Models.DefinitionStorage; using WorkflowCore.Models.DefinitionStorage.v1; using WorkflowCore.Exceptions; diff --git a/src/WorkflowCore/Exceptions/WorkflowDefinitionLoadException.cs b/src/WorkflowCore/Exceptions/WorkflowDefinitionLoadException.cs index 0ceb5ab38..6cc35d6f0 100644 --- a/src/WorkflowCore/Exceptions/WorkflowDefinitionLoadException.cs +++ b/src/WorkflowCore/Exceptions/WorkflowDefinitionLoadException.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Exceptions { diff --git a/src/WorkflowCore/Interface/IActivityController.cs b/src/WorkflowCore/Interface/IActivityController.cs index 99e5c1737..46464e8e8 100644 --- a/src/WorkflowCore/Interface/IActivityController.cs +++ b/src/WorkflowCore/Interface/IActivityController.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; namespace WorkflowCore.Interface diff --git a/src/WorkflowCore/Interface/ICancellationProcessor.cs b/src/WorkflowCore/Interface/ICancellationProcessor.cs index 09d37969b..f6cbb31fa 100644 --- a/src/WorkflowCore/Interface/ICancellationProcessor.cs +++ b/src/WorkflowCore/Interface/ICancellationProcessor.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Models; namespace WorkflowCore.Interface diff --git a/src/WorkflowCore/Interface/ILifeCycleEventHub.cs b/src/WorkflowCore/Interface/ILifeCycleEventHub.cs index 70663effc..6ce3fb177 100644 --- a/src/WorkflowCore/Interface/ILifeCycleEventHub.cs +++ b/src/WorkflowCore/Interface/ILifeCycleEventHub.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using WorkflowCore.Models.LifeCycleEvents; diff --git a/src/WorkflowCore/Interface/ILifeCycleEventPublisher.cs b/src/WorkflowCore/Interface/ILifeCycleEventPublisher.cs index 273c26456..db703659a 100644 --- a/src/WorkflowCore/Interface/ILifeCycleEventPublisher.cs +++ b/src/WorkflowCore/Interface/ILifeCycleEventPublisher.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Interface diff --git a/src/WorkflowCore/Interface/ISearchIndex.cs b/src/WorkflowCore/Interface/ISearchIndex.cs index 64bd075f6..0aea590a8 100644 --- a/src/WorkflowCore/Interface/ISearchIndex.cs +++ b/src/WorkflowCore/Interface/ISearchIndex.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using WorkflowCore.Models; using WorkflowCore.Models.Search; diff --git a/src/WorkflowCore/Interface/ISearchable.cs b/src/WorkflowCore/Interface/ISearchable.cs index 9ceec14e2..e769f916c 100644 --- a/src/WorkflowCore/Interface/ISearchable.cs +++ b/src/WorkflowCore/Interface/ISearchable.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Interface { diff --git a/src/WorkflowCore/Interface/IStepBuilder.cs b/src/WorkflowCore/Interface/IStepBuilder.cs index c654a9d20..e20d585c5 100644 --- a/src/WorkflowCore/Interface/IStepBuilder.cs +++ b/src/WorkflowCore/Interface/IStepBuilder.cs @@ -1,8 +1,6 @@ using System; -using System.Collections; using System.Linq.Expressions; using WorkflowCore.Models; -using WorkflowCore.Primitives; namespace WorkflowCore.Interface { diff --git a/src/WorkflowCore/Interface/IStepParameter.cs b/src/WorkflowCore/Interface/IStepParameter.cs index 049ecdb25..5bbbb2fbf 100644 --- a/src/WorkflowCore/Interface/IStepParameter.cs +++ b/src/WorkflowCore/Interface/IStepParameter.cs @@ -1,7 +1,4 @@ -using WorkflowCore.Interface; -using WorkflowCore.Models; - -namespace WorkflowCore.Interface +namespace WorkflowCore.Interface { public interface IStepParameter { diff --git a/src/WorkflowCore/Interface/IWorkflowController.cs b/src/WorkflowCore/Interface/IWorkflowController.cs index 89e7fc3c6..924925d2c 100644 --- a/src/WorkflowCore/Interface/IWorkflowController.cs +++ b/src/WorkflowCore/Interface/IWorkflowController.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; namespace WorkflowCore.Interface diff --git a/src/WorkflowCore/Interface/IWorkflowErrorHandler.cs b/src/WorkflowCore/Interface/IWorkflowErrorHandler.cs index 479176627..fa0734118 100644 --- a/src/WorkflowCore/Interface/IWorkflowErrorHandler.cs +++ b/src/WorkflowCore/Interface/IWorkflowErrorHandler.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using WorkflowCore.Models; namespace WorkflowCore.Interface diff --git a/src/WorkflowCore/Models/ActionParameter.cs b/src/WorkflowCore/Models/ActionParameter.cs index f6a3e4723..762a865e8 100644 --- a/src/WorkflowCore/Models/ActionParameter.cs +++ b/src/WorkflowCore/Models/ActionParameter.cs @@ -1,7 +1,5 @@ using System; using System.Linq; -using System.Linq.Expressions; -using System.Reflection; using WorkflowCore.Interface; namespace WorkflowCore.Models diff --git a/src/WorkflowCore/Models/ActivityResult.cs b/src/WorkflowCore/Models/ActivityResult.cs index 212ca92d8..f3a828a08 100644 --- a/src/WorkflowCore/Models/ActivityResult.cs +++ b/src/WorkflowCore/Models/ActivityResult.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models { diff --git a/src/WorkflowCore/Models/ExecutionPointerCollection.cs b/src/WorkflowCore/Models/ExecutionPointerCollection.cs index 90e64348c..f125db5a8 100644 --- a/src/WorkflowCore/Models/ExecutionPointerCollection.cs +++ b/src/WorkflowCore/Models/ExecutionPointerCollection.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; namespace WorkflowCore.Models { diff --git a/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs b/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs index afab488b0..f72283e9a 100755 --- a/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs +++ b/src/WorkflowCore/Models/LifeCycleEvents/LifeCycleEvent.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.LifeCycleEvents { diff --git a/src/WorkflowCore/Models/LifeCycleEvents/StepCompleted.cs b/src/WorkflowCore/Models/LifeCycleEvents/StepCompleted.cs index 38a68697d..3d44be288 100644 --- a/src/WorkflowCore/Models/LifeCycleEvents/StepCompleted.cs +++ b/src/WorkflowCore/Models/LifeCycleEvents/StepCompleted.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.LifeCycleEvents { diff --git a/src/WorkflowCore/Models/LifeCycleEvents/StepStarted.cs b/src/WorkflowCore/Models/LifeCycleEvents/StepStarted.cs index a76c50e0f..183dd1a7b 100644 --- a/src/WorkflowCore/Models/LifeCycleEvents/StepStarted.cs +++ b/src/WorkflowCore/Models/LifeCycleEvents/StepStarted.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.LifeCycleEvents { diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowCompleted.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowCompleted.cs index 4b3d8a2e2..e83da1827 100644 --- a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowCompleted.cs +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowCompleted.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.LifeCycleEvents { diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowError.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowError.cs index 55f273d63..e24849fcf 100644 --- a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowError.cs +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowError.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.LifeCycleEvents { diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowResumed.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowResumed.cs index 88f2ee603..1c400b84d 100644 --- a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowResumed.cs +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowResumed.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.LifeCycleEvents { diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowStarted.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowStarted.cs index c461ee8aa..60b906de8 100644 --- a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowStarted.cs +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowStarted.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.LifeCycleEvents { diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowSuspended.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowSuspended.cs index 39d19a7a8..ccadff82d 100644 --- a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowSuspended.cs +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowSuspended.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.LifeCycleEvents { diff --git a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowTerminated.cs b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowTerminated.cs index 07ff8f75f..778c23493 100644 --- a/src/WorkflowCore/Models/LifeCycleEvents/WorkflowTerminated.cs +++ b/src/WorkflowCore/Models/LifeCycleEvents/WorkflowTerminated.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.LifeCycleEvents { diff --git a/src/WorkflowCore/Models/MemberMapParameter.cs b/src/WorkflowCore/Models/MemberMapParameter.cs index 052f0bc1a..e5273986d 100644 --- a/src/WorkflowCore/Models/MemberMapParameter.cs +++ b/src/WorkflowCore/Models/MemberMapParameter.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Linq.Expressions; -using System.Reflection; using WorkflowCore.Interface; namespace WorkflowCore.Models diff --git a/src/WorkflowCore/Models/Search/Page.cs b/src/WorkflowCore/Models/Search/Page.cs index 63658fd37..412688a28 100644 --- a/src/WorkflowCore/Models/Search/Page.cs +++ b/src/WorkflowCore/Models/Search/Page.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.Search { diff --git a/src/WorkflowCore/Models/Search/SearchFilter.cs b/src/WorkflowCore/Models/Search/SearchFilter.cs index b4d7ff66b..e7491806c 100644 --- a/src/WorkflowCore/Models/Search/SearchFilter.cs +++ b/src/WorkflowCore/Models/Search/SearchFilter.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq.Expressions; -using System.Text; namespace WorkflowCore.Models.Search { diff --git a/src/WorkflowCore/Models/Search/StepInfo.cs b/src/WorkflowCore/Models/Search/StepInfo.cs index b17ccedd0..6b9a97d8f 100644 --- a/src/WorkflowCore/Models/Search/StepInfo.cs +++ b/src/WorkflowCore/Models/Search/StepInfo.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Models.Search { diff --git a/src/WorkflowCore/Models/Search/WorkflowSearchResult.cs b/src/WorkflowCore/Models/Search/WorkflowSearchResult.cs index 6870d36ab..1cb6a3c9a 100644 --- a/src/WorkflowCore/Models/Search/WorkflowSearchResult.cs +++ b/src/WorkflowCore/Models/Search/WorkflowSearchResult.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Reflection; -using WorkflowCore.Interface; namespace WorkflowCore.Models.Search { diff --git a/src/WorkflowCore/Models/WorkflowDefinition.cs b/src/WorkflowCore/Models/WorkflowDefinition.cs index 207457e19..0cea6cc40 100644 --- a/src/WorkflowCore/Models/WorkflowDefinition.cs +++ b/src/WorkflowCore/Models/WorkflowDefinition.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace WorkflowCore.Models diff --git a/src/WorkflowCore/Models/WorkflowInstance.cs b/src/WorkflowCore/Models/WorkflowInstance.cs index b75b284fc..ca683d74a 100644 --- a/src/WorkflowCore/Models/WorkflowInstance.cs +++ b/src/WorkflowCore/Models/WorkflowInstance.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; namespace WorkflowCore.Models diff --git a/src/WorkflowCore/Models/WorkflowStep.cs b/src/WorkflowCore/Models/WorkflowStep.cs index 08ad1c525..814219916 100644 --- a/src/WorkflowCore/Models/WorkflowStep.cs +++ b/src/WorkflowCore/Models/WorkflowStep.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; -using System.Reflection; using WorkflowCore.Interface; namespace WorkflowCore.Models diff --git a/src/WorkflowCore/Models/WorkflowStepCollection.cs b/src/WorkflowCore/Models/WorkflowStepCollection.cs index 90c2e4710..68346620e 100644 --- a/src/WorkflowCore/Models/WorkflowStepCollection.cs +++ b/src/WorkflowCore/Models/WorkflowStepCollection.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; namespace WorkflowCore.Models { diff --git a/src/WorkflowCore/Primitives/ContainerStepBody.cs b/src/WorkflowCore/Primitives/ContainerStepBody.cs index 30ad9b644..46da6a0b0 100644 --- a/src/WorkflowCore/Primitives/ContainerStepBody.cs +++ b/src/WorkflowCore/Primitives/ContainerStepBody.cs @@ -1,5 +1,4 @@ using System.Linq; -using System.Collections.Generic; using WorkflowCore.Models; namespace WorkflowCore.Primitives diff --git a/src/WorkflowCore/Primitives/SagaContainer.cs b/src/WorkflowCore/Primitives/SagaContainer.cs index 2034f6bd4..93470cdbb 100644 --- a/src/WorkflowCore/Primitives/SagaContainer.cs +++ b/src/WorkflowCore/Primitives/SagaContainer.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using WorkflowCore.Exceptions; -using WorkflowCore.Interface; +using WorkflowCore.Interface; using WorkflowCore.Models; namespace WorkflowCore.Primitives diff --git a/src/WorkflowCore/Properties/AssemblyInfo.cs b/src/WorkflowCore/Properties/AssemblyInfo.cs index d692f8bbb..2d7f37b38 100644 --- a/src/WorkflowCore/Properties/AssemblyInfo.cs +++ b/src/WorkflowCore/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 94634db96..760a89d41 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -1,12 +1,8 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Services; using WorkflowCore.Models; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.ObjectPool; using WorkflowCore.Primitives; using WorkflowCore.Services.BackgroundTasks; diff --git a/src/WorkflowCore/Services/ActivityController.cs b/src/WorkflowCore/Services/ActivityController.cs index 04af6ef9f..e37481521 100644 --- a/src/WorkflowCore/Services/ActivityController.cs +++ b/src/WorkflowCore/Services/ActivityController.cs @@ -1,5 +1,4 @@ using System; -using System.Dynamic; using System.Linq; using System.Text; using System.Threading; diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 6ce3776b5..9cda8206c 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -2,7 +2,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.ObjectPool; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/WorkflowCore/Services/CancellationProcessor.cs b/src/WorkflowCore/Services/CancellationProcessor.cs index 3ee58e287..4e56de9ea 100644 --- a/src/WorkflowCore/Services/CancellationProcessor.cs +++ b/src/WorkflowCore/Services/CancellationProcessor.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs index fd27db8ca..9d64b689c 100644 --- a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/src/WorkflowCore/Services/DefaultProviders/NullSearchIndex.cs b/src/WorkflowCore/Services/DefaultProviders/NullSearchIndex.cs index db66ccf57..40d13fa9c 100644 --- a/src/WorkflowCore/Services/DefaultProviders/NullSearchIndex.cs +++ b/src/WorkflowCore/Services/DefaultProviders/NullSearchIndex.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs b/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs index 40a826b91..007b51efb 100644 --- a/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs +++ b/src/WorkflowCore/Services/DefaultProviders/SingleNodeEventHub.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using WorkflowCore.Interface; diff --git a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs index 6b3084a89..466fc85e6 100644 --- a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index dfc13af36..028f0d112 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; -using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Services.ErrorHandlers { diff --git a/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs index 0a4d54810..84fbe5bd3 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/RetryHandler.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; -using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Services.ErrorHandlers { diff --git a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs index 7c5fad05b..20ff0bab3 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Models.LifeCycleEvents; diff --git a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs index 89fb05eae..573bd7f17 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Models.LifeCycleEvents; diff --git a/src/WorkflowCore/Services/ExecutionPointerFactory.cs b/src/WorkflowCore/Services/ExecutionPointerFactory.cs index 937e83d99..5bd4b2846 100644 --- a/src/WorkflowCore/Services/ExecutionPointerFactory.cs +++ b/src/WorkflowCore/Services/ExecutionPointerFactory.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs index 25334bcd2..ffebb332e 100644 --- a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs +++ b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs @@ -1,8 +1,6 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models.LifeCycleEvents; diff --git a/src/WorkflowCore/Services/SyncWorkflowRunner.cs b/src/WorkflowCore/Services/SyncWorkflowRunner.cs index 293c976a0..1ff8326fa 100644 --- a/src/WorkflowCore/Services/SyncWorkflowRunner.cs +++ b/src/WorkflowCore/Services/SyncWorkflowRunner.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using WorkflowCore.Exceptions; using WorkflowCore.Interface; using WorkflowCore.Models; -using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Services { diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 576c1b981..4cc591de5 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index aa08ba727..2a7fcc330 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -6,9 +6,6 @@ using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.Models; -using System.Reflection; -using Microsoft.Extensions.DependencyInjection; -using WorkflowCore.Exceptions; using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Services diff --git a/src/extensions/WorkflowCore.Users/Interface/IUserTaskBuilder.cs b/src/extensions/WorkflowCore.Users/Interface/IUserTaskBuilder.cs index e1df05ac6..9451ced29 100644 --- a/src/extensions/WorkflowCore.Users/Interface/IUserTaskBuilder.cs +++ b/src/extensions/WorkflowCore.Users/Interface/IUserTaskBuilder.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; using System.Linq.Expressions; -using System.Text; using WorkflowCore.Interface; -using WorkflowCore.Primitives; using WorkflowCore.Users.Primitives; -using WorkflowCore.Users.Services; namespace WorkflowCore.Users.Interface { diff --git a/src/extensions/WorkflowCore.Users/Interface/IUserTaskReturnBuilder.cs b/src/extensions/WorkflowCore.Users/Interface/IUserTaskReturnBuilder.cs index b6eed494c..37ee22b18 100644 --- a/src/extensions/WorkflowCore.Users/Interface/IUserTaskReturnBuilder.cs +++ b/src/extensions/WorkflowCore.Users/Interface/IUserTaskReturnBuilder.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Primitives; diff --git a/src/extensions/WorkflowCore.Users/Models/OpenUserAction.cs b/src/extensions/WorkflowCore.Users/Models/OpenUserAction.cs index 5d2ed8b8a..1452452a1 100644 --- a/src/extensions/WorkflowCore.Users/Models/OpenUserAction.cs +++ b/src/extensions/WorkflowCore.Users/Models/OpenUserAction.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace WorkflowCore.Users.Models { diff --git a/src/extensions/WorkflowCore.Users/Models/UserAction.cs b/src/extensions/WorkflowCore.Users/Models/UserAction.cs index 61d5b4c48..d4d769824 100644 --- a/src/extensions/WorkflowCore.Users/Models/UserAction.cs +++ b/src/extensions/WorkflowCore.Users/Models/UserAction.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace WorkflowCore.Users.Models { diff --git a/src/extensions/WorkflowCore.Users/Models/UserStep.cs b/src/extensions/WorkflowCore.Users/Models/UserStep.cs index e1f4b0c09..b8c147f65 100644 --- a/src/extensions/WorkflowCore.Users/Models/UserStep.cs +++ b/src/extensions/WorkflowCore.Users/Models/UserStep.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs b/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs index 0b3b348e4..e4238b9cc 100644 --- a/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs +++ b/src/extensions/WorkflowCore.Users/Models/UserStepContainer.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/extensions/WorkflowCore.Users/Primitives/Escalate.cs b/src/extensions/WorkflowCore.Users/Primitives/Escalate.cs index bdd8c6518..7a400bb87 100644 --- a/src/extensions/WorkflowCore.Users/Primitives/Escalate.cs +++ b/src/extensions/WorkflowCore.Users/Primitives/Escalate.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/extensions/WorkflowCore.Users/Primitives/EscalateStep.cs b/src/extensions/WorkflowCore.Users/Primitives/EscalateStep.cs index 7ed21b795..a56e000d3 100644 --- a/src/extensions/WorkflowCore.Users/Primitives/EscalateStep.cs +++ b/src/extensions/WorkflowCore.Users/Primitives/EscalateStep.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Text; -using WorkflowCore.Interface; using WorkflowCore.Models; namespace WorkflowCore.Users.Primitives diff --git a/src/extensions/WorkflowCore.Users/Primitives/UserTaskStep.cs b/src/extensions/WorkflowCore.Users/Primitives/UserTaskStep.cs index f12696d41..cc568019b 100644 --- a/src/extensions/WorkflowCore.Users/Primitives/UserTaskStep.cs +++ b/src/extensions/WorkflowCore.Users/Primitives/UserTaskStep.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/extensions/WorkflowCore.Users/Properties/AssemblyInfo.cs b/src/extensions/WorkflowCore.Users/Properties/AssemblyInfo.cs index 4a776a642..fa59636da 100644 --- a/src/extensions/WorkflowCore.Users/Properties/AssemblyInfo.cs +++ b/src/extensions/WorkflowCore.Users/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs b/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs index 3330c123b..2b337c1fd 100644 --- a/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs +++ b/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Threading.Tasks; -using WorkflowCore.Interface; using WorkflowCore.Models; -using WorkflowCore.Primitives; using WorkflowCore.Services; using WorkflowCore.Users.Interface; using WorkflowCore.Users.Models; diff --git a/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowHostExtensions.cs b/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowHostExtensions.cs index 89b753f52..606d5592f 100644 --- a/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowHostExtensions.cs +++ b/src/extensions/WorkflowCore.Users/ServiceExtensions/WorkflowHostExtensions.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; using System.Threading.Tasks; -using WorkflowCore.Interface; using WorkflowCore.Models; -using WorkflowCore.Services; using WorkflowCore.Users.Models; using WorkflowCore.Users.Primitives; diff --git a/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs b/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs index 5ea5da8db..ccba7b239 100644 --- a/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs +++ b/src/extensions/WorkflowCore.Users/Services/UserTaskBuilder.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq.Expressions; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Primitives; diff --git a/src/extensions/WorkflowCore.Users/Services/UserTaskReturnBuilder.cs b/src/extensions/WorkflowCore.Users/Services/UserTaskReturnBuilder.cs index 70438ba8b..6bc06be4b 100644 --- a/src/extensions/WorkflowCore.Users/Services/UserTaskReturnBuilder.cs +++ b/src/extensions/WorkflowCore.Users/Services/UserTaskReturnBuilder.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Primitives; diff --git a/src/extensions/WorkflowCore.WebAPI/Controllers/EventsController.cs b/src/extensions/WorkflowCore.WebAPI/Controllers/EventsController.cs index f2ec00029..4930aa22d 100644 --- a/src/extensions/WorkflowCore.WebAPI/Controllers/EventsController.cs +++ b/src/extensions/WorkflowCore.WebAPI/Controllers/EventsController.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using WorkflowCore.Interface; diff --git a/src/extensions/WorkflowCore.WebAPI/Controllers/WorkflowsController.cs b/src/extensions/WorkflowCore.WebAPI/Controllers/WorkflowsController.cs index f4200b13c..3c88df3fb 100644 --- a/src/extensions/WorkflowCore.WebAPI/Controllers/WorkflowsController.cs +++ b/src/extensions/WorkflowCore.WebAPI/Controllers/WorkflowsController.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; diff --git a/src/extensions/WorkflowCore.WebAPI/Properties/AssemblyInfo.cs b/src/extensions/WorkflowCore.WebAPI/Properties/AssemblyInfo.cs index 3ad8e3878..b5a5d4e35 100644 --- a/src/extensions/WorkflowCore.WebAPI/Properties/AssemblyInfo.cs +++ b/src/extensions/WorkflowCore.WebAPI/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/extensions/WorkflowCore.WebAPI/ServiceCollectionExtensions.cs b/src/extensions/WorkflowCore.WebAPI/ServiceCollectionExtensions.cs index bfe6bbd73..3a2ead3db 100644 --- a/src/extensions/WorkflowCore.WebAPI/ServiceCollectionExtensions.cs +++ b/src/extensions/WorkflowCore.WebAPI/ServiceCollectionExtensions.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace Microsoft.Extensions.DependencyInjection { diff --git a/src/providers/WorkflowCore.LockProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.LockProviders.SqlServer/ServiceCollectionExtensions.cs index e93985658..1bdcab46c 100644 --- a/src/providers/WorkflowCore.LockProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.LockProviders.SqlServer/ServiceCollectionExtensions.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using WorkflowCore.LockProviders.SqlServer; using WorkflowCore.Models; diff --git a/src/providers/WorkflowCore.LockProviders.SqlServer/SqlLockProvider.cs b/src/providers/WorkflowCore.LockProviders.SqlServer/SqlLockProvider.cs index cfeafd36c..92795d871 100644 --- a/src/providers/WorkflowCore.LockProviders.SqlServer/SqlLockProvider.cs +++ b/src/providers/WorkflowCore.LockProviders.SqlServer/SqlLockProvider.cs @@ -5,7 +5,6 @@ using WorkflowCore.Interface; using System.Data; using System.Collections.Generic; -using System.Collections.Concurrent; using System.Threading; namespace WorkflowCore.LockProviders.SqlServer diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs index ae0659968..c978131d1 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Models; using WorkflowCore.Persistence.EntityFramework.Models; diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Interfaces/IWorkflowDbContextFactory.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Interfaces/IWorkflowDbContextFactory.cs index 574f9c39f..61dfb5214 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Interfaces/IWorkflowDbContextFactory.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Interfaces/IWorkflowDbContextFactory.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Persistence.EntityFramework.Services; namespace WorkflowCore.Persistence.EntityFramework.Interfaces diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedEvent.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedEvent.cs index d60090c83..832bee16b 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedEvent.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedEvent.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; -using System.Threading.Tasks; namespace WorkflowCore.Persistence.EntityFramework.Models { diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionError.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionError.cs index 1a181a821..7741aa767 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionError.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionError.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using System.Threading.Tasks; -using WorkflowCore.Models; namespace WorkflowCore.Persistence.EntityFramework.Models { diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointer.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointer.cs index 984e27735..c7b5b48eb 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointer.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointer.cs @@ -3,7 +3,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Models; namespace WorkflowCore.Persistence.EntityFramework.Models diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointerCollection.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointerCollection.cs index c8950803b..cbff64146 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointerCollection.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExecutionPointerCollection.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Persistence.EntityFramework.Models { diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExtensionAttribute.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExtensionAttribute.cs index 3786e9e5a..5a382d8a1 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExtensionAttribute.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedExtensionAttribute.cs @@ -1,10 +1,7 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using System.Threading.Tasks; -using WorkflowCore.Models; namespace WorkflowCore.Persistence.EntityFramework.Models { diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs index f8fe0cca0..fcc10f005 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedSubscription.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using System.Threading.Tasks; namespace WorkflowCore.Persistence.EntityFramework.Models { diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedWorkflow.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedWorkflow.cs index b212bc0c9..5c427a988 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedWorkflow.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedWorkflow.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Models; namespace WorkflowCore.Persistence.EntityFramework.Models diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Properties/AssemblyInfo.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Properties/AssemblyInfo.cs index 9371ecd66..d147caeeb 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Properties/AssemblyInfo.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs index 3af7354de..a616dda01 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs @@ -2,14 +2,10 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Persistence.EntityFramework.Models; using WorkflowCore.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; using WorkflowCore.Persistence.EntityFramework.Interfaces; namespace WorkflowCore.Persistence.EntityFramework.Services diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs index 69f83cf96..7b80db3da 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs @@ -1,13 +1,7 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using WorkflowCore.Interface; -using WorkflowCore.Models; using WorkflowCore.Persistence.EntityFramework.Models; namespace WorkflowCore.Persistence.EntityFramework.Services diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/ConfigOptions.cs b/src/providers/WorkflowCore.Persistence.MongoDB/ConfigOptions.cs index c5b9cddbd..0efd90b42 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/ConfigOptions.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/ConfigOptions.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace WorkflowCore.Persistence.MongoDB { diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Properties/AssemblyInfo.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Properties/AssemblyInfo.cs index df8354da4..4c1a32df7 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Properties/AssemblyInfo.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs index a5d3ebb5c..34e7857c8 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs @@ -1,8 +1,6 @@ using MongoDB.Driver; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Persistence.MongoDB.Services; diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/AssemblyQualifiedDiscriminatorConvention.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/AssemblyQualifiedDiscriminatorConvention.cs index 64908727e..b9845f6f8 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/AssemblyQualifiedDiscriminatorConvention.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/AssemblyQualifiedDiscriminatorConvention.cs @@ -1,8 +1,6 @@ using MongoDB.Bson.Serialization.Conventions; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.IO; diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs index 17a84e5d7..c72c16199 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs @@ -2,8 +2,6 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using WorkflowCore.Persistence.MySQL; namespace WorkflowCore.Persistence.MySQL.Migrations { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.cs index c6f6d73f3..c74d0e6f1 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170126230815_InitialDatabase.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.cs index 77fc5a589..6be6f105b 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170312161610_Events.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.cs index 8e44ba8aa..ddbe6630a 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170507214430_ControlStructures.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; namespace WorkflowCore.Persistence.PostgreSQL.Migrations diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.cs index 4a5806080..0544d31e5 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170519231452_PersistOutcome.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; namespace WorkflowCore.Persistence.PostgreSQL.Migrations diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.cs index 997299d80..a7058ff61 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20170722200412_WfReference.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; namespace WorkflowCore.Persistence.PostgreSQL.Migrations diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.cs index 439bfd655..023ca1919 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20171223020844_StepScope.cs @@ -1,6 +1,5 @@ using Microsoft.EntityFrameworkCore.Migrations; using System; -using System.Collections.Generic; namespace WorkflowCore.Persistence.PostgreSQL.Migrations { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs index 71ab82c32..6def22497 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs @@ -2,9 +2,7 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using WorkflowCore.Persistence.PostgreSQL; namespace WorkflowCore.Persistence.PostgreSQL.Migrations { diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs index 818a75be2..17e83e958 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using WorkflowCore.Persistence.EntityFramework.Models; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs index e0758dfab..98a633e1d 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContextFactory.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Persistence.EntityFramework.Interfaces; using WorkflowCore.Persistence.EntityFramework.Services; diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Properties/AssemblyInfo.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Properties/AssemblyInfo.cs index 036cf6f38..01280515a 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Properties/AssemblyInfo.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs index 8df41c092..9366c3936 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/ServiceCollectionExtensions.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Persistence.EntityFramework.Services; diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/RavenStoreOptions.cs b/src/providers/WorkflowCore.Persistence.RavenDB/RavenStoreOptions.cs index 00a420668..9208e0ce1 100644 --- a/src/providers/WorkflowCore.Persistence.RavenDB/RavenStoreOptions.cs +++ b/src/providers/WorkflowCore.Persistence.RavenDB/RavenStoreOptions.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Persistence.RavenDB { diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.RavenDB/ServiceCollectionExtensions.cs index 4b7d12926..fb05b9ba3 100644 --- a/src/providers/WorkflowCore.Persistence.RavenDB/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.RavenDB/ServiceCollectionExtensions.cs @@ -1,9 +1,5 @@ -using Raven.Client.Documents.Operations; -using System; -using System.Collections.Generic; -using System.Text; +using System; using WorkflowCore.Models; -using Raven.Client; using Raven.Client.Documents; using System.Security.Cryptography.X509Certificates; using WorkflowCore.Persistence.RavenDB.Services; diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavenDbIndexes.cs b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavenDbIndexes.cs index 75f5ab106..77b4dbae5 100644 --- a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavenDbIndexes.cs +++ b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavenDbIndexes.cs @@ -1,7 +1,5 @@ using Raven.Client.Documents.Indexes; using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Models; namespace WorkflowCore.Persistence.RavenDB.Services diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs index 66d38b5aa..7d1f87ddd 100644 --- a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs @@ -1,5 +1,4 @@ using Raven.Client.Documents; -using Raven.Client.Documents.Indexes; using Raven.Client.Documents.Linq; using Raven.Client.Documents.Operations; using System; diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs b/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs index 70fd0210b..f799fd6a5 100644 --- a/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs +++ b/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs @@ -1,13 +1,9 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; -using Raven.Client; using System.Threading.Tasks; using Raven.Client.Documents; using System.Linq; -using Raven.Client.Extensions; using Raven.Client.Documents.Operations; using Raven.Client.Documents.Queries; diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170126230839_InitialDatabase.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170126230839_InitialDatabase.cs index 12153905d..cc13adb32 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170126230839_InitialDatabase.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170126230839_InitialDatabase.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Metadata; diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170312161610_Events.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170312161610_Events.cs index bc4565904..c3854150f 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170312161610_Events.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170312161610_Events.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Metadata; diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170507214430_ControlStructures.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170507214430_ControlStructures.cs index 18e8c5b38..df0f88ab3 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170507214430_ControlStructures.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170507214430_ControlStructures.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; namespace WorkflowCore.Persistence.SqlServer.Migrations diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170519231452_PersistOutcome.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170519231452_PersistOutcome.cs index b4fd690ae..dd472520e 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170519231452_PersistOutcome.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170519231452_PersistOutcome.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; namespace WorkflowCore.Persistence.SqlServer.Migrations diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170722195832_WfReference.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170722195832_WfReference.cs index 1e94cad6d..285e7d944 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170722195832_WfReference.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20170722195832_WfReference.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; namespace WorkflowCore.Persistence.SqlServer.Migrations diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20171223020645_StepScope.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20171223020645_StepScope.cs index e9ff500e1..c7e1e4ec6 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20171223020645_StepScope.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20171223020645_StepScope.cs @@ -1,6 +1,5 @@ using Microsoft.EntityFrameworkCore.Migrations; using System; -using System.Collections.Generic; namespace WorkflowCore.Persistence.SqlServer.Migrations { diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs index 1b5c63cc1..2e32d838d 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs @@ -2,9 +2,6 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using WorkflowCore.Persistence.SqlServer; namespace WorkflowCore.Persistence.SqlServer.Migrations { diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Properties/AssemblyInfo.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Properties/AssemblyInfo.cs index afdd6ce15..26131f807 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Properties/AssemblyInfo.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs index 009f1edb7..0a54b1ea2 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/ServiceCollectionExtensions.cs @@ -2,7 +2,6 @@ using System.Data.Common; using WorkflowCore.Interface; using WorkflowCore.Models; -using WorkflowCore.Persistence.EntityFramework.Interfaces; using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.SqlServer; diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs index 8cb6d57a8..066aae73f 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlContextFactory.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Data.Common; using Microsoft.EntityFrameworkCore; using WorkflowCore.Persistence.EntityFramework.Interfaces; diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs index 7383cce3e..6e2656a5b 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using WorkflowCore.Persistence.EntityFramework.Models; diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/Properties/AssemblyInfo.cs b/src/providers/WorkflowCore.Persistence.Sqlite/Properties/AssemblyInfo.cs index 02fd59231..04f04aacc 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/Properties/AssemblyInfo.cs +++ b/src/providers/WorkflowCore.Persistence.Sqlite/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs index f137c33b5..0d5dd5a8e 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.Sqlite/ServiceCollectionExtensions.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Persistence.EntityFramework.Services; diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs b/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs index 7a9737c4e..1b5a6c469 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs +++ b/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs @@ -1,9 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Persistence.EntityFramework.Models; using WorkflowCore.Persistence.EntityFramework.Services; diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContextFactory.cs b/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContextFactory.cs index 94c9e4df8..9d24d688c 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContextFactory.cs +++ b/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContextFactory.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Persistence.EntityFramework.Interfaces; using WorkflowCore.Persistence.EntityFramework.Services; diff --git a/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisStreamConsumer.cs b/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisStreamConsumer.cs index 41e48b013..d4d66614f 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisStreamConsumer.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisStreamConsumer.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Amazon.Kinesis.Model; diff --git a/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs b/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs index 3a4cee3a0..f3cb37510 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Interface/IKinesisTracker.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; namespace WorkflowCore.Providers.AWS.Interface diff --git a/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs index 62f9cf38e..daf5b6889 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ModelExtensions.cs @@ -1,11 +1,8 @@ using Amazon.DynamoDBv2.Model; -using Amazon.Util; using System; using System.Collections.Generic; using System.Linq; -using System.Text; using Newtonsoft.Json; -using Newtonsoft.Json.Bson; using WorkflowCore.Models; namespace WorkflowCore.Providers.AWS diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs index a6bb72662..3f381c8c3 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using WorkflowCore.Providers.AWS.Interface; diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index 69ba8eaf1..6c27546ab 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using WorkflowCore.Providers.AWS.Interface; using WorkflowCore.Interface; diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs index 5b0830ce0..99d43d94f 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; using System.Threading.Tasks; using Amazon; using Amazon.Kinesis; diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs index 8d6a04b8e..9c5548420 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; -using System.Threading; using System.Threading.Tasks; using Amazon; using Amazon.DynamoDBv2; diff --git a/src/providers/WorkflowCore.Providers.Azure/Models/ControlledLock.cs b/src/providers/WorkflowCore.Providers.Azure/Models/ControlledLock.cs index f32a1b0cd..5b0343456 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Models/ControlledLock.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Models/ControlledLock.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.WindowsAzure.Storage.Blob; namespace WorkflowCore.Providers.Azure.Models diff --git a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs index 85579f7fc..57e43fd40 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedEvent.cs @@ -1,6 +1,5 @@ using Newtonsoft.Json; using System; -using System.Collections.Generic; using WorkflowCore.Models; namespace WorkflowCore.Providers.Azure.Models diff --git a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs index 55482e17d..d1966eea6 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedSubscription.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Newtonsoft.Json; using WorkflowCore.Models; diff --git a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs index bd480fb71..f8af8228d 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Models/PersistedWorkflow.cs @@ -1,6 +1,5 @@ using Newtonsoft.Json; using System; -using System.Collections.Generic; using WorkflowCore.Models; namespace WorkflowCore.Providers.Azure.Models diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs b/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs index 9b6d0bdb1..c52e823c7 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/AzureLockManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs index 5b104ab30..cbd76d1f7 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/AzureStorageQueueProvider.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs index 8ca697fc2..a30ca8f27 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.Azure.Cosmos; using WorkflowCore.Interface; diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs index ff1c2e187..ab86cc3f6 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.ObjectModel; using System.Threading.Tasks; using Microsoft.Azure.Cosmos; using Microsoft.Extensions.Logging; diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs b/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs index 21e285207..8195bd53a 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/Services/ElasticsearchIndexer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Reflection; using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs index ccc96a98c..514ba21a3 100644 --- a/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs @@ -1,6 +1,5 @@ using System; using Microsoft.Extensions.Logging; -using StackExchange.Redis; using WorkflowCore.Models; using WorkflowCore.Providers.Redis.Services; diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs index 2cf12e1cc..bb595309d 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLifeCycleEventHub.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Newtonsoft.Json; diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs index 006f531d6..469ca2763 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index 66ca58d72..4c26b38b2 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Newtonsoft.Json; diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs index 1281bfbdf..460f3d741 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Properties/AssemblyInfo.cs b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Properties/AssemblyInfo.cs index 4a1926468..53e1beb8e 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Properties/AssemblyInfo.cs +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs index 8a84f99c6..64322ad50 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/Services/RabbitMQProvider.cs @@ -1,15 +1,12 @@ using Newtonsoft.Json; using RabbitMQ.Client; -using RabbitMQ.Client.Events; using System; -using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; -using WorkflowCore.Models; using WorkflowCore.QueueProviders.RabbitMQ.Interfaces; namespace WorkflowCore.QueueProviders.RabbitMQ.Services diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs index 50f1173e1..19b173d5c 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Interfaces/ISqlServerQueueProviderMigrator.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; namespace WorkflowCore.QueueProviders.SqlServer.Interfaces diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs index 1915f487c..c323b9d03 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Models/QueueConfig.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.QueueProviders.SqlServer.Models { diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs index ada9b580a..56f4f7fc2 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/ServiceCollectionExtensions.cs @@ -1,8 +1,6 @@ #region using using System; -using Microsoft.Extensions.DependencyInjection; - using WorkflowCore.Models; using WorkflowCore.QueueProviders.SqlServer; using WorkflowCore.QueueProviders.SqlServer.Interfaces; diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs index 9e54393a1..4cbcd7dac 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/QueueConfigProvider.cs @@ -1,7 +1,6 @@ #region using using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using WorkflowCore.Interface; diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs index 3ff92713d..5c77342fe 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlCommandExecutor.cs @@ -1,8 +1,6 @@ #region using using System; -using System.Collections.Generic; -using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Linq; diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs index a28f894e6..18a27b920 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/Services/SqlServerQueueProviderMigrator.cs @@ -3,7 +3,6 @@ using System; using System.Data.SqlClient; using System.Linq; -using System.Text.RegularExpressions; using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.QueueProviders.SqlServer.Interfaces; diff --git a/src/samples/WorkflowCore.Sample01/HelloWorldWorkflow.cs b/src/samples/WorkflowCore.Sample01/HelloWorldWorkflow.cs index 74887e1e5..f268b6307 100644 --- a/src/samples/WorkflowCore.Sample01/HelloWorldWorkflow.cs +++ b/src/samples/WorkflowCore.Sample01/HelloWorldWorkflow.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; -using WorkflowCore.Models; using WorkflowCore.Sample01.Steps; namespace WorkflowCore.Sample01 diff --git a/src/samples/WorkflowCore.Sample01/Program.cs b/src/samples/WorkflowCore.Sample01/Program.cs index c25838dbe..6577f5ba0 100644 --- a/src/samples/WorkflowCore.Sample01/Program.cs +++ b/src/samples/WorkflowCore.Sample01/Program.cs @@ -1,12 +1,8 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Sample01.Steps; -using WorkflowCore.Services; namespace WorkflowCore.Sample01 { diff --git a/src/samples/WorkflowCore.Sample01/Properties/AssemblyInfo.cs b/src/samples/WorkflowCore.Sample01/Properties/AssemblyInfo.cs index 32a0f2b49..be2722eae 100644 --- a/src/samples/WorkflowCore.Sample01/Properties/AssemblyInfo.cs +++ b/src/samples/WorkflowCore.Sample01/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/samples/WorkflowCore.Sample01/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.Sample01/Steps/GoodbyeWorld.cs index 8c94778f7..048b57bb1 100644 --- a/src/samples/WorkflowCore.Sample01/Steps/GoodbyeWorld.cs +++ b/src/samples/WorkflowCore.Sample01/Steps/GoodbyeWorld.cs @@ -1,8 +1,6 @@ using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample01/Steps/HelloWorld.cs b/src/samples/WorkflowCore.Sample01/Steps/HelloWorld.cs index a5a09534a..415ad08f2 100644 --- a/src/samples/WorkflowCore.Sample01/Steps/HelloWorld.cs +++ b/src/samples/WorkflowCore.Sample01/Steps/HelloWorld.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample02/Program.cs b/src/samples/WorkflowCore.Sample02/Program.cs index 3c1a65bcf..22dd3c196 100644 --- a/src/samples/WorkflowCore.Sample02/Program.cs +++ b/src/samples/WorkflowCore.Sample02/Program.cs @@ -1,11 +1,7 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; -using WorkflowCore.Services; namespace WorkflowCore.Sample02 diff --git a/src/samples/WorkflowCore.Sample02/Properties/AssemblyInfo.cs b/src/samples/WorkflowCore.Sample02/Properties/AssemblyInfo.cs index 5b02eba4c..f677ae82a 100644 --- a/src/samples/WorkflowCore.Sample02/Properties/AssemblyInfo.cs +++ b/src/samples/WorkflowCore.Sample02/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/samples/WorkflowCore.Sample02/SimpleDecisionWorkflow.cs b/src/samples/WorkflowCore.Sample02/SimpleDecisionWorkflow.cs index b32fa3c55..91a0323f2 100644 --- a/src/samples/WorkflowCore.Sample02/SimpleDecisionWorkflow.cs +++ b/src/samples/WorkflowCore.Sample02/SimpleDecisionWorkflow.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Sample02.Steps; diff --git a/src/samples/WorkflowCore.Sample02/Steps/CustomMessage.cs b/src/samples/WorkflowCore.Sample02/Steps/CustomMessage.cs index 2514a7384..efb3fb662 100644 --- a/src/samples/WorkflowCore.Sample02/Steps/CustomMessage.cs +++ b/src/samples/WorkflowCore.Sample02/Steps/CustomMessage.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample02/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.Sample02/Steps/GoodbyeWorld.cs index 8e09fed44..c082e57c9 100644 --- a/src/samples/WorkflowCore.Sample02/Steps/GoodbyeWorld.cs +++ b/src/samples/WorkflowCore.Sample02/Steps/GoodbyeWorld.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample02/Steps/HelloWorld.cs b/src/samples/WorkflowCore.Sample02/Steps/HelloWorld.cs index 151d92a3e..884b01525 100644 --- a/src/samples/WorkflowCore.Sample02/Steps/HelloWorld.cs +++ b/src/samples/WorkflowCore.Sample02/Steps/HelloWorld.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample02/Steps/RandomOutput.cs b/src/samples/WorkflowCore.Sample02/Steps/RandomOutput.cs index 90bbdbddf..80c285161 100644 --- a/src/samples/WorkflowCore.Sample02/Steps/RandomOutput.cs +++ b/src/samples/WorkflowCore.Sample02/Steps/RandomOutput.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample03/MyDataClass.cs b/src/samples/WorkflowCore.Sample03/MyDataClass.cs index a3b51429c..3f0cead60 100644 --- a/src/samples/WorkflowCore.Sample03/MyDataClass.cs +++ b/src/samples/WorkflowCore.Sample03/MyDataClass.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using WorkflowCore.Interface; namespace WorkflowCore.Sample03 { diff --git a/src/samples/WorkflowCore.Sample03/PassingDataWorkflow.cs b/src/samples/WorkflowCore.Sample03/PassingDataWorkflow.cs index c96f3dc03..56307b42e 100644 --- a/src/samples/WorkflowCore.Sample03/PassingDataWorkflow.cs +++ b/src/samples/WorkflowCore.Sample03/PassingDataWorkflow.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Sample03.Steps; diff --git a/src/samples/WorkflowCore.Sample03/Program.cs b/src/samples/WorkflowCore.Sample03/Program.cs index 96c06d45d..813e5dfd7 100644 --- a/src/samples/WorkflowCore.Sample03/Program.cs +++ b/src/samples/WorkflowCore.Sample03/Program.cs @@ -1,12 +1,8 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Debug; using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; -using WorkflowCore.Services; namespace WorkflowCore.Sample03 diff --git a/src/samples/WorkflowCore.Sample03/Properties/AssemblyInfo.cs b/src/samples/WorkflowCore.Sample03/Properties/AssemblyInfo.cs index 324e13bc7..3965ce4cd 100644 --- a/src/samples/WorkflowCore.Sample03/Properties/AssemblyInfo.cs +++ b/src/samples/WorkflowCore.Sample03/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/samples/WorkflowCore.Sample03/Steps/AddNumbers.cs b/src/samples/WorkflowCore.Sample03/Steps/AddNumbers.cs index 209229812..a8209d227 100644 --- a/src/samples/WorkflowCore.Sample03/Steps/AddNumbers.cs +++ b/src/samples/WorkflowCore.Sample03/Steps/AddNumbers.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using WorkflowCore.Interface; diff --git a/src/samples/WorkflowCore.Sample03/Steps/CustomMessage.cs b/src/samples/WorkflowCore.Sample03/Steps/CustomMessage.cs index ce83fcdc0..810092b30 100644 --- a/src/samples/WorkflowCore.Sample03/Steps/CustomMessage.cs +++ b/src/samples/WorkflowCore.Sample03/Steps/CustomMessage.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample03/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.Sample03/Steps/GoodbyeWorld.cs index 4a700c768..c7f985288 100644 --- a/src/samples/WorkflowCore.Sample03/Steps/GoodbyeWorld.cs +++ b/src/samples/WorkflowCore.Sample03/Steps/GoodbyeWorld.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample03/Steps/HelloWorld.cs b/src/samples/WorkflowCore.Sample03/Steps/HelloWorld.cs index ccd93045c..e156b13ba 100644 --- a/src/samples/WorkflowCore.Sample03/Steps/HelloWorld.cs +++ b/src/samples/WorkflowCore.Sample03/Steps/HelloWorld.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample04/EventSampleWorkflow.cs b/src/samples/WorkflowCore.Sample04/EventSampleWorkflow.cs index d07a5ac03..b78eaa751 100644 --- a/src/samples/WorkflowCore.Sample04/EventSampleWorkflow.cs +++ b/src/samples/WorkflowCore.Sample04/EventSampleWorkflow.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Sample04.Steps; diff --git a/src/samples/WorkflowCore.Sample04/MyDataClass.cs b/src/samples/WorkflowCore.Sample04/MyDataClass.cs index 0863891a2..719459c33 100644 --- a/src/samples/WorkflowCore.Sample04/MyDataClass.cs +++ b/src/samples/WorkflowCore.Sample04/MyDataClass.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace WorkflowCore.Sample04 { diff --git a/src/samples/WorkflowCore.Sample04/Program.cs b/src/samples/WorkflowCore.Sample04/Program.cs index 09e80b737..0e1895d44 100644 --- a/src/samples/WorkflowCore.Sample04/Program.cs +++ b/src/samples/WorkflowCore.Sample04/Program.cs @@ -1,18 +1,8 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using MongoDB.Driver; using StackExchange.Redis; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; -using Amazon; -using Amazon.Runtime; using WorkflowCore.Interface; -using WorkflowCore.Persistence.MongoDB.Services; -using WorkflowCore.Services; -using Amazon.DynamoDBv2; -using Amazon.SQS; namespace WorkflowCore.Sample04 { diff --git a/src/samples/WorkflowCore.Sample04/Properties/AssemblyInfo.cs b/src/samples/WorkflowCore.Sample04/Properties/AssemblyInfo.cs index 2bc13d42e..e51bcce10 100644 --- a/src/samples/WorkflowCore.Sample04/Properties/AssemblyInfo.cs +++ b/src/samples/WorkflowCore.Sample04/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/samples/WorkflowCore.Sample04/Steps/CustomMessage.cs b/src/samples/WorkflowCore.Sample04/Steps/CustomMessage.cs index 4d96503fb..678f22731 100644 --- a/src/samples/WorkflowCore.Sample04/Steps/CustomMessage.cs +++ b/src/samples/WorkflowCore.Sample04/Steps/CustomMessage.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample05/DeferSampleWorkflow.cs b/src/samples/WorkflowCore.Sample05/DeferSampleWorkflow.cs index 08380429c..ff9223ae9 100644 --- a/src/samples/WorkflowCore.Sample05/DeferSampleWorkflow.cs +++ b/src/samples/WorkflowCore.Sample05/DeferSampleWorkflow.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Sample05.Steps; diff --git a/src/samples/WorkflowCore.Sample05/Program.cs b/src/samples/WorkflowCore.Sample05/Program.cs index 274e10232..a0e727633 100644 --- a/src/samples/WorkflowCore.Sample05/Program.cs +++ b/src/samples/WorkflowCore.Sample05/Program.cs @@ -1,13 +1,7 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using MongoDB.Driver; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; -using WorkflowCore.Persistence.MongoDB.Services; -using WorkflowCore.Services; namespace WorkflowCore.Sample05 { diff --git a/src/samples/WorkflowCore.Sample05/Properties/AssemblyInfo.cs b/src/samples/WorkflowCore.Sample05/Properties/AssemblyInfo.cs index 660ed42d2..a510661da 100644 --- a/src/samples/WorkflowCore.Sample05/Properties/AssemblyInfo.cs +++ b/src/samples/WorkflowCore.Sample05/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/samples/WorkflowCore.Sample05/Steps/SleepStep.cs b/src/samples/WorkflowCore.Sample05/Steps/SleepStep.cs index f2d1ef94c..c11cfb6f7 100644 --- a/src/samples/WorkflowCore.Sample05/Steps/SleepStep.cs +++ b/src/samples/WorkflowCore.Sample05/Steps/SleepStep.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample06/MultipleOutcomeWorkflow.cs b/src/samples/WorkflowCore.Sample06/MultipleOutcomeWorkflow.cs index 8b792d968..5f7661455 100644 --- a/src/samples/WorkflowCore.Sample06/MultipleOutcomeWorkflow.cs +++ b/src/samples/WorkflowCore.Sample06/MultipleOutcomeWorkflow.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Sample06.Steps; diff --git a/src/samples/WorkflowCore.Sample06/Program.cs b/src/samples/WorkflowCore.Sample06/Program.cs index ba08aa071..4612f92cc 100644 --- a/src/samples/WorkflowCore.Sample06/Program.cs +++ b/src/samples/WorkflowCore.Sample06/Program.cs @@ -1,13 +1,7 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using MongoDB.Driver; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; -using WorkflowCore.Persistence.MongoDB.Services; -using WorkflowCore.Services; namespace WorkflowCore.Sample06 { diff --git a/src/samples/WorkflowCore.Sample06/Properties/AssemblyInfo.cs b/src/samples/WorkflowCore.Sample06/Properties/AssemblyInfo.cs index 20a134e87..abc7e334f 100644 --- a/src/samples/WorkflowCore.Sample06/Properties/AssemblyInfo.cs +++ b/src/samples/WorkflowCore.Sample06/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/samples/WorkflowCore.Sample06/Steps/RandomOutput.cs b/src/samples/WorkflowCore.Sample06/Steps/RandomOutput.cs index 79587280d..dc686abec 100644 --- a/src/samples/WorkflowCore.Sample06/Steps/RandomOutput.cs +++ b/src/samples/WorkflowCore.Sample06/Steps/RandomOutput.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample06/Steps/TaskA.cs b/src/samples/WorkflowCore.Sample06/Steps/TaskA.cs index 67d20cf76..17b8cd6d3 100644 --- a/src/samples/WorkflowCore.Sample06/Steps/TaskA.cs +++ b/src/samples/WorkflowCore.Sample06/Steps/TaskA.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample06/Steps/TaskB.cs b/src/samples/WorkflowCore.Sample06/Steps/TaskB.cs index cd2554eae..c230fb836 100644 --- a/src/samples/WorkflowCore.Sample06/Steps/TaskB.cs +++ b/src/samples/WorkflowCore.Sample06/Steps/TaskB.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample06/Steps/TaskC.cs b/src/samples/WorkflowCore.Sample06/Steps/TaskC.cs index 39770f8e4..ec840b843 100644 --- a/src/samples/WorkflowCore.Sample06/Steps/TaskC.cs +++ b/src/samples/WorkflowCore.Sample06/Steps/TaskC.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample06/Steps/TaskD.cs b/src/samples/WorkflowCore.Sample06/Steps/TaskD.cs index 2f40005ea..cd4ff1940 100644 --- a/src/samples/WorkflowCore.Sample06/Steps/TaskD.cs +++ b/src/samples/WorkflowCore.Sample06/Steps/TaskD.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample07/Program.cs b/src/samples/WorkflowCore.Sample07/Program.cs index 57b417b72..b89645580 100644 --- a/src/samples/WorkflowCore.Sample07/Program.cs +++ b/src/samples/WorkflowCore.Sample07/Program.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; namespace WorkflowCore.Sample07 diff --git a/src/samples/WorkflowCore.Sample07/Startup.cs b/src/samples/WorkflowCore.Sample07/Startup.cs index 42abb05cb..80264f6d2 100644 --- a/src/samples/WorkflowCore.Sample07/Startup.cs +++ b/src/samples/WorkflowCore.Sample07/Startup.cs @@ -1,10 +1,7 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using WorkflowCore.Interface; diff --git a/src/samples/WorkflowCore.Sample08/HumanWorkflow.cs b/src/samples/WorkflowCore.Sample08/HumanWorkflow.cs index b6e00849d..b2b48b1c8 100644 --- a/src/samples/WorkflowCore.Sample08/HumanWorkflow.cs +++ b/src/samples/WorkflowCore.Sample08/HumanWorkflow.cs @@ -1,10 +1,7 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; -using WorkflowCore.Users.Models; namespace WorkflowCore.Sample08 { diff --git a/src/samples/WorkflowCore.Sample08/Program.cs b/src/samples/WorkflowCore.Sample08/Program.cs index 4da29c6b9..970834c88 100644 --- a/src/samples/WorkflowCore.Sample08/Program.cs +++ b/src/samples/WorkflowCore.Sample08/Program.cs @@ -1,10 +1,7 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Threading.Tasks; using WorkflowCore.Interface; namespace WorkflowCore.Sample08 diff --git a/src/samples/WorkflowCore.Sample08/Properties/AssemblyInfo.cs b/src/samples/WorkflowCore.Sample08/Properties/AssemblyInfo.cs index 079de7d9e..2f6266e9f 100644 --- a/src/samples/WorkflowCore.Sample08/Properties/AssemblyInfo.cs +++ b/src/samples/WorkflowCore.Sample08/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/samples/WorkflowCore.Sample09/ForEachWorkflow.cs b/src/samples/WorkflowCore.Sample09/ForEachWorkflow.cs index e9a5cd937..ba462bac1 100644 --- a/src/samples/WorkflowCore.Sample09/ForEachWorkflow.cs +++ b/src/samples/WorkflowCore.Sample09/ForEachWorkflow.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; -using WorkflowCore.Models; namespace WorkflowCore.Sample09 { diff --git a/src/samples/WorkflowCore.Sample09/Program.cs b/src/samples/WorkflowCore.Sample09/Program.cs index 52516fca8..f98fc1547 100644 --- a/src/samples/WorkflowCore.Sample09/Program.cs +++ b/src/samples/WorkflowCore.Sample09/Program.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; using WorkflowCore.Interface; diff --git a/src/samples/WorkflowCore.Sample09/Steps/DisplayContext.cs b/src/samples/WorkflowCore.Sample09/Steps/DisplayContext.cs index 9782a32bc..0d9815038 100644 --- a/src/samples/WorkflowCore.Sample09/Steps/DisplayContext.cs +++ b/src/samples/WorkflowCore.Sample09/Steps/DisplayContext.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample09/Steps/DoSomething.cs b/src/samples/WorkflowCore.Sample09/Steps/DoSomething.cs index 57d0f556a..9636c486c 100644 --- a/src/samples/WorkflowCore.Sample09/Steps/DoSomething.cs +++ b/src/samples/WorkflowCore.Sample09/Steps/DoSomething.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample09/Steps/SayGoodbye.cs b/src/samples/WorkflowCore.Sample09/Steps/SayGoodbye.cs index 9836d5f44..c3aaec908 100644 --- a/src/samples/WorkflowCore.Sample09/Steps/SayGoodbye.cs +++ b/src/samples/WorkflowCore.Sample09/Steps/SayGoodbye.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample09/Steps/SayHello.cs b/src/samples/WorkflowCore.Sample09/Steps/SayHello.cs index 94ba8768a..653b28af1 100644 --- a/src/samples/WorkflowCore.Sample09/Steps/SayHello.cs +++ b/src/samples/WorkflowCore.Sample09/Steps/SayHello.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs b/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs index 879156d06..2c1a335a7 100644 --- a/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs +++ b/src/samples/WorkflowCore.Sample09s/ForEachSyncWorkflow.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; -using WorkflowCore.Models; namespace WorkflowCore.Sample09s { diff --git a/src/samples/WorkflowCore.Sample09s/Program.cs b/src/samples/WorkflowCore.Sample09s/Program.cs index 9fd4dcb0f..09e04abb8 100644 --- a/src/samples/WorkflowCore.Sample09s/Program.cs +++ b/src/samples/WorkflowCore.Sample09s/Program.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; using WorkflowCore.Interface; diff --git a/src/samples/WorkflowCore.Sample09s/Steps/DisplayContext.cs b/src/samples/WorkflowCore.Sample09s/Steps/DisplayContext.cs index d7ef216b3..012cda0c6 100644 --- a/src/samples/WorkflowCore.Sample09s/Steps/DisplayContext.cs +++ b/src/samples/WorkflowCore.Sample09s/Steps/DisplayContext.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample09s/Steps/DoSomething.cs b/src/samples/WorkflowCore.Sample09s/Steps/DoSomething.cs index 39344b389..859a13eab 100644 --- a/src/samples/WorkflowCore.Sample09s/Steps/DoSomething.cs +++ b/src/samples/WorkflowCore.Sample09s/Steps/DoSomething.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample09s/Steps/SayGoodbye.cs b/src/samples/WorkflowCore.Sample09s/Steps/SayGoodbye.cs index 34c74103b..1789b74b2 100644 --- a/src/samples/WorkflowCore.Sample09s/Steps/SayGoodbye.cs +++ b/src/samples/WorkflowCore.Sample09s/Steps/SayGoodbye.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample09s/Steps/SayHello.cs b/src/samples/WorkflowCore.Sample09s/Steps/SayHello.cs index 1597910f5..ebec30eed 100644 --- a/src/samples/WorkflowCore.Sample09s/Steps/SayHello.cs +++ b/src/samples/WorkflowCore.Sample09s/Steps/SayHello.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample10/Program.cs b/src/samples/WorkflowCore.Sample10/Program.cs index aa401baa2..5fa612f8e 100644 --- a/src/samples/WorkflowCore.Sample10/Program.cs +++ b/src/samples/WorkflowCore.Sample10/Program.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; using WorkflowCore.Interface; diff --git a/src/samples/WorkflowCore.Sample10/Steps/DoSomething.cs b/src/samples/WorkflowCore.Sample10/Steps/DoSomething.cs index 646b929a6..389bd86e3 100644 --- a/src/samples/WorkflowCore.Sample10/Steps/DoSomething.cs +++ b/src/samples/WorkflowCore.Sample10/Steps/DoSomething.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample10/Steps/IncrementStep.cs b/src/samples/WorkflowCore.Sample10/Steps/IncrementStep.cs index cb7177bba..2e000956c 100644 --- a/src/samples/WorkflowCore.Sample10/Steps/IncrementStep.cs +++ b/src/samples/WorkflowCore.Sample10/Steps/IncrementStep.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample10/Steps/SayGoodbye.cs b/src/samples/WorkflowCore.Sample10/Steps/SayGoodbye.cs index 48ec493ef..f28c50dcf 100644 --- a/src/samples/WorkflowCore.Sample10/Steps/SayGoodbye.cs +++ b/src/samples/WorkflowCore.Sample10/Steps/SayGoodbye.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample10/Steps/SayHello.cs b/src/samples/WorkflowCore.Sample10/Steps/SayHello.cs index d0e96745b..0b552ea83 100644 --- a/src/samples/WorkflowCore.Sample10/Steps/SayHello.cs +++ b/src/samples/WorkflowCore.Sample10/Steps/SayHello.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample10/WhileWorkflow.cs b/src/samples/WorkflowCore.Sample10/WhileWorkflow.cs index b65156edc..bf4ce8ccc 100644 --- a/src/samples/WorkflowCore.Sample10/WhileWorkflow.cs +++ b/src/samples/WorkflowCore.Sample10/WhileWorkflow.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; -using WorkflowCore.Models; namespace WorkflowCore.Sample10 { diff --git a/src/samples/WorkflowCore.Sample11/IfWorkflow.cs b/src/samples/WorkflowCore.Sample11/IfWorkflow.cs index 64b31fb56..2b9ef2265 100644 --- a/src/samples/WorkflowCore.Sample11/IfWorkflow.cs +++ b/src/samples/WorkflowCore.Sample11/IfWorkflow.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; -using WorkflowCore.Models; namespace WorkflowCore.Sample11 { diff --git a/src/samples/WorkflowCore.Sample11/Program.cs b/src/samples/WorkflowCore.Sample11/Program.cs index e409b400a..ea920b195 100644 --- a/src/samples/WorkflowCore.Sample11/Program.cs +++ b/src/samples/WorkflowCore.Sample11/Program.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; using WorkflowCore.Interface; diff --git a/src/samples/WorkflowCore.Sample11/Steps/PrintMessage.cs b/src/samples/WorkflowCore.Sample11/Steps/PrintMessage.cs index d2f1e013c..16e709773 100644 --- a/src/samples/WorkflowCore.Sample11/Steps/PrintMessage.cs +++ b/src/samples/WorkflowCore.Sample11/Steps/PrintMessage.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample11/Steps/SayGoodbye.cs b/src/samples/WorkflowCore.Sample11/Steps/SayGoodbye.cs index ba7601881..388f5acbf 100644 --- a/src/samples/WorkflowCore.Sample11/Steps/SayGoodbye.cs +++ b/src/samples/WorkflowCore.Sample11/Steps/SayGoodbye.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample11/Steps/SayHello.cs b/src/samples/WorkflowCore.Sample11/Steps/SayHello.cs index b8b5f0ad3..ed5c2a17d 100644 --- a/src/samples/WorkflowCore.Sample11/Steps/SayHello.cs +++ b/src/samples/WorkflowCore.Sample11/Steps/SayHello.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample12/OutcomeWorkflow.cs b/src/samples/WorkflowCore.Sample12/OutcomeWorkflow.cs index 61f1f98cd..aa58e0a74 100644 --- a/src/samples/WorkflowCore.Sample12/OutcomeWorkflow.cs +++ b/src/samples/WorkflowCore.Sample12/OutcomeWorkflow.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; -using WorkflowCore.Models; namespace WorkflowCore.Sample12 { diff --git a/src/samples/WorkflowCore.Sample12/Program.cs b/src/samples/WorkflowCore.Sample12/Program.cs index 937265e4f..c3b6e211a 100644 --- a/src/samples/WorkflowCore.Sample12/Program.cs +++ b/src/samples/WorkflowCore.Sample12/Program.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; using WorkflowCore.Interface; diff --git a/src/samples/WorkflowCore.Sample12/Steps/DetermineSomething.cs b/src/samples/WorkflowCore.Sample12/Steps/DetermineSomething.cs index 44f99d41b..807162e4e 100644 --- a/src/samples/WorkflowCore.Sample12/Steps/DetermineSomething.cs +++ b/src/samples/WorkflowCore.Sample12/Steps/DetermineSomething.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample12/Steps/PrintMessage.cs b/src/samples/WorkflowCore.Sample12/Steps/PrintMessage.cs index c655831d2..3809d0226 100644 --- a/src/samples/WorkflowCore.Sample12/Steps/PrintMessage.cs +++ b/src/samples/WorkflowCore.Sample12/Steps/PrintMessage.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample12/Steps/SayGoodbye.cs b/src/samples/WorkflowCore.Sample12/Steps/SayGoodbye.cs index ba4c581a3..7c2be887c 100644 --- a/src/samples/WorkflowCore.Sample12/Steps/SayGoodbye.cs +++ b/src/samples/WorkflowCore.Sample12/Steps/SayGoodbye.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample12/Steps/SayHello.cs b/src/samples/WorkflowCore.Sample12/Steps/SayHello.cs index b232aa55b..5fe4b5c3e 100644 --- a/src/samples/WorkflowCore.Sample12/Steps/SayHello.cs +++ b/src/samples/WorkflowCore.Sample12/Steps/SayHello.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample13/ParallelWorkflow.cs b/src/samples/WorkflowCore.Sample13/ParallelWorkflow.cs index 8b4ea0d0a..83209bcc3 100644 --- a/src/samples/WorkflowCore.Sample13/ParallelWorkflow.cs +++ b/src/samples/WorkflowCore.Sample13/ParallelWorkflow.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; -using WorkflowCore.Models; namespace WorkflowCore.Sample13 { diff --git a/src/samples/WorkflowCore.Sample13/Program.cs b/src/samples/WorkflowCore.Sample13/Program.cs index 9f5a22e7a..6e0ba9d0a 100644 --- a/src/samples/WorkflowCore.Sample13/Program.cs +++ b/src/samples/WorkflowCore.Sample13/Program.cs @@ -1,11 +1,6 @@ using System; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using WorkflowCore.Interface; -using System.Threading; -using System.Threading.Tasks; -using System.Security.Cryptography; -using System.Collections.Generic; namespace WorkflowCore.Sample13 { diff --git a/src/samples/WorkflowCore.Sample13/Steps/PrintMessage.cs b/src/samples/WorkflowCore.Sample13/Steps/PrintMessage.cs index dbb59578f..0cd23dc03 100644 --- a/src/samples/WorkflowCore.Sample13/Steps/PrintMessage.cs +++ b/src/samples/WorkflowCore.Sample13/Steps/PrintMessage.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample13/Steps/SayGoodbye.cs b/src/samples/WorkflowCore.Sample13/Steps/SayGoodbye.cs index b7779d3b8..40b851e56 100644 --- a/src/samples/WorkflowCore.Sample13/Steps/SayGoodbye.cs +++ b/src/samples/WorkflowCore.Sample13/Steps/SayGoodbye.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample13/Steps/SayHello.cs b/src/samples/WorkflowCore.Sample13/Steps/SayHello.cs index bf174e0c0..591025768 100644 --- a/src/samples/WorkflowCore.Sample13/Steps/SayHello.cs +++ b/src/samples/WorkflowCore.Sample13/Steps/SayHello.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample14/Program.cs b/src/samples/WorkflowCore.Sample14/Program.cs index b2ecabb2d..f12ae2621 100644 --- a/src/samples/WorkflowCore.Sample14/Program.cs +++ b/src/samples/WorkflowCore.Sample14/Program.cs @@ -1,6 +1,5 @@ using System; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using WorkflowCore.Interface; namespace WorkflowCore.Sample14 diff --git a/src/samples/WorkflowCore.Sample14/RecurSampleWorkflow.cs b/src/samples/WorkflowCore.Sample14/RecurSampleWorkflow.cs index d3d91f05b..862624479 100644 --- a/src/samples/WorkflowCore.Sample14/RecurSampleWorkflow.cs +++ b/src/samples/WorkflowCore.Sample14/RecurSampleWorkflow.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; -using WorkflowCore.Models; namespace WorkflowCore.Sample14 { diff --git a/src/samples/WorkflowCore.Sample15/HelloWorldWorkflow.cs b/src/samples/WorkflowCore.Sample15/HelloWorldWorkflow.cs index 7c2ed4984..4b1744add 100644 --- a/src/samples/WorkflowCore.Sample15/HelloWorldWorkflow.cs +++ b/src/samples/WorkflowCore.Sample15/HelloWorldWorkflow.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; -using WorkflowCore.Models; using WorkflowCore.Sample15.Steps; namespace WorkflowCore.Sample15 diff --git a/src/samples/WorkflowCore.Sample15/Program.cs b/src/samples/WorkflowCore.Sample15/Program.cs index 04d4c2a58..76d539baf 100644 --- a/src/samples/WorkflowCore.Sample15/Program.cs +++ b/src/samples/WorkflowCore.Sample15/Program.cs @@ -1,6 +1,5 @@ using System; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.Sample15.Steps; using WorkflowCore.Sample15.Services; diff --git a/src/samples/WorkflowCore.Sample15/Services/MyService.cs b/src/samples/WorkflowCore.Sample15/Services/MyService.cs index 290805efd..8b8b322cf 100644 --- a/src/samples/WorkflowCore.Sample15/Services/MyService.cs +++ b/src/samples/WorkflowCore.Sample15/Services/MyService.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.Sample15.Services { diff --git a/src/samples/WorkflowCore.Sample15/Steps/DoSomething.cs b/src/samples/WorkflowCore.Sample15/Steps/DoSomething.cs index 5aeafdb3e..e8f7bff3b 100644 --- a/src/samples/WorkflowCore.Sample15/Steps/DoSomething.cs +++ b/src/samples/WorkflowCore.Sample15/Steps/DoSomething.cs @@ -1,8 +1,5 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; +using System; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Sample15.Services; diff --git a/src/samples/WorkflowCore.Sample15/Steps/HelloWorld.cs b/src/samples/WorkflowCore.Sample15/Steps/HelloWorld.cs index 6d32d532d..21dec311b 100644 --- a/src/samples/WorkflowCore.Sample15/Steps/HelloWorld.cs +++ b/src/samples/WorkflowCore.Sample15/Steps/HelloWorld.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample16/Program.cs b/src/samples/WorkflowCore.Sample16/Program.cs index 6ca03139d..db085a6d9 100644 --- a/src/samples/WorkflowCore.Sample16/Program.cs +++ b/src/samples/WorkflowCore.Sample16/Program.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; using WorkflowCore.Interface; diff --git a/src/samples/WorkflowCore.Sample16/ScheduleWorkflow.cs b/src/samples/WorkflowCore.Sample16/ScheduleWorkflow.cs index 47d4e72bd..69878f1b8 100644 --- a/src/samples/WorkflowCore.Sample16/ScheduleWorkflow.cs +++ b/src/samples/WorkflowCore.Sample16/ScheduleWorkflow.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; namespace WorkflowCore.Sample16 diff --git a/src/samples/WorkflowCore.Sample17/CompensatingWorkflow.cs b/src/samples/WorkflowCore.Sample17/CompensatingWorkflow.cs index 94db0806e..00a0ead8d 100644 --- a/src/samples/WorkflowCore.Sample17/CompensatingWorkflow.cs +++ b/src/samples/WorkflowCore.Sample17/CompensatingWorkflow.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; using WorkflowCore.Interface; using WorkflowCore.Sample17.Steps; diff --git a/src/samples/WorkflowCore.Sample18/ActivityWorkflow.cs b/src/samples/WorkflowCore.Sample18/ActivityWorkflow.cs index 126e2be15..22b32a782 100644 --- a/src/samples/WorkflowCore.Sample18/ActivityWorkflow.cs +++ b/src/samples/WorkflowCore.Sample18/ActivityWorkflow.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; using WorkflowCore.Interface; using WorkflowCore.Sample18.Steps; diff --git a/src/samples/WorkflowCore.Sample18/Program.cs b/src/samples/WorkflowCore.Sample18/Program.cs index 295701499..aa7d3f2f7 100644 --- a/src/samples/WorkflowCore.Sample18/Program.cs +++ b/src/samples/WorkflowCore.Sample18/Program.cs @@ -1,7 +1,5 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Console; -using Microsoft.Extensions.Logging.Debug; using System; using WorkflowCore.Interface; diff --git a/src/samples/WorkflowCore.Sample18/Steps/CustomMessage.cs b/src/samples/WorkflowCore.Sample18/Steps/CustomMessage.cs index 0d9803de3..69f1dd42c 100644 --- a/src/samples/WorkflowCore.Sample18/Steps/CustomMessage.cs +++ b/src/samples/WorkflowCore.Sample18/Steps/CustomMessage.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample18/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.Sample18/Steps/GoodbyeWorld.cs index dffce0d6b..b42f3e913 100644 --- a/src/samples/WorkflowCore.Sample18/Steps/GoodbyeWorld.cs +++ b/src/samples/WorkflowCore.Sample18/Steps/GoodbyeWorld.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample18/Steps/HelloWorld.cs b/src/samples/WorkflowCore.Sample18/Steps/HelloWorld.cs index 8440b8cbe..8378bb7cd 100644 --- a/src/samples/WorkflowCore.Sample18/Steps/HelloWorld.cs +++ b/src/samples/WorkflowCore.Sample18/Steps/HelloWorld.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.Sample19/Program.cs b/src/samples/WorkflowCore.Sample19/Program.cs index 1d6e38a48..6d3074744 100644 --- a/src/samples/WorkflowCore.Sample19/Program.cs +++ b/src/samples/WorkflowCore.Sample19/Program.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; -using Microsoft.Extensions.Logging.Console; using WorkflowCore.Interface; using WorkflowCore.Sample19.Middleware; using WorkflowCore.Sample19.Steps; diff --git a/src/samples/WorkflowCore.TestSample01/NUnitTest.cs b/src/samples/WorkflowCore.TestSample01/NUnitTest.cs index d01aaf5cb..2b2b898e6 100644 --- a/src/samples/WorkflowCore.TestSample01/NUnitTest.cs +++ b/src/samples/WorkflowCore.TestSample01/NUnitTest.cs @@ -1,8 +1,6 @@ using FluentAssertions; using NUnit.Framework; using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Models; using WorkflowCore.Testing; using WorkflowCore.TestSample01.Workflow; diff --git a/src/samples/WorkflowCore.TestSample01/Workflow/AddNumbers.cs b/src/samples/WorkflowCore.TestSample01/Workflow/AddNumbers.cs index a45f06f9d..0ec30e24b 100644 --- a/src/samples/WorkflowCore.TestSample01/Workflow/AddNumbers.cs +++ b/src/samples/WorkflowCore.TestSample01/Workflow/AddNumbers.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/src/samples/WorkflowCore.TestSample01/Workflow/MyDataClass.cs b/src/samples/WorkflowCore.TestSample01/Workflow/MyDataClass.cs index 1db1d9273..84f1abf54 100644 --- a/src/samples/WorkflowCore.TestSample01/Workflow/MyDataClass.cs +++ b/src/samples/WorkflowCore.TestSample01/Workflow/MyDataClass.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.TestSample01.Workflow { diff --git a/src/samples/WorkflowCore.TestSample01/Workflow/MyWorkflow.cs b/src/samples/WorkflowCore.TestSample01/Workflow/MyWorkflow.cs index bf485e559..67b390873 100644 --- a/src/samples/WorkflowCore.TestSample01/Workflow/MyWorkflow.cs +++ b/src/samples/WorkflowCore.TestSample01/Workflow/MyWorkflow.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; namespace WorkflowCore.TestSample01.Workflow diff --git a/test/Docker.Testify/DockerSetup.cs b/test/Docker.Testify/DockerSetup.cs index 1f6b48b07..71bb76e26 100644 --- a/test/Docker.Testify/DockerSetup.cs +++ b/test/Docker.Testify/DockerSetup.cs @@ -3,12 +3,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Runtime.InteropServices; -using System.Text; -using System.Threading; using System.Threading.Tasks; namespace Docker.Testify diff --git a/test/Docker.Testify/PortsInUseException.cs b/test/Docker.Testify/PortsInUseException.cs index 48a897c22..f4ad7ae07 100644 --- a/test/Docker.Testify/PortsInUseException.cs +++ b/test/Docker.Testify/PortsInUseException.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace Docker.Testify { diff --git a/test/ScratchPad/ElasticTest.cs b/test/ScratchPad/ElasticTest.cs index 6d8ae43f0..6401bda70 100644 --- a/test/ScratchPad/ElasticTest.cs +++ b/test/ScratchPad/ElasticTest.cs @@ -1,16 +1,7 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; +using System; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; -using WorkflowCore.Models; -using System.Text; -using Amazon; -using Amazon.DynamoDBv2; -using Amazon.Runtime; using Nest; using WorkflowCore.Models.Search; diff --git a/test/ScratchPad/Program.cs b/test/ScratchPad/Program.cs index 3c0ee5f76..0d44810b9 100644 --- a/test/ScratchPad/Program.cs +++ b/test/ScratchPad/Program.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.Logging; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -7,8 +6,6 @@ using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Models; -using System.Text; -using WorkflowCore.Services.DefinitionStorage; namespace ScratchPad { diff --git a/test/WorkflowCore.IntegrationTests/Properties/AssemblyInfo.cs b/test/WorkflowCore.IntegrationTests/Properties/AssemblyInfo.cs index 2a6d71492..f0355ab5a 100644 --- a/test/WorkflowCore.IntegrationTests/Properties/AssemblyInfo.cs +++ b/test/WorkflowCore.IntegrationTests/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs index ae7b372ab..1e082f4db 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/AttachScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/AttachScenario.cs index 9f7f93245..1c1fe7bdd 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/AttachScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/AttachScenario.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; using FluentAssertions; -using System.Threading; using WorkflowCore.Testing; namespace WorkflowCore.IntegrationTests.Scenarios diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/BaseScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/BaseScenario.cs index 4e70b446c..db8211b44 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/BaseScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/BaseScenario.cs @@ -1,8 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; namespace WorkflowCore.IntegrationTests.Scenarios diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/BasicScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/BasicScenario.cs index 77b660088..5d5e0d37b 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/BasicScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/BasicScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/CancelledEventScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/CancelledEventScenario.cs index b0675f3f2..61b7a6540 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/CancelledEventScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/CancelledEventScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario.cs index a07d1e44a..2a6cc8da4 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs b/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs index d27564f07..bbb997d14 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/CompensationScenario2.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs index d5adab795..d8a12724d 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs index 81a782264..fd100fe2d 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DiScenario.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.DependencyInjection; using Autofac.Extensions.DependencyInjection; using Autofac; -using System.Diagnostics; namespace WorkflowCore.IntegrationTests.Scenarios { diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/EndStepScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/EndStepScenario.cs index f950d58b3..f177423a8 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/EndStepScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/EndStepScenario.cs @@ -1,7 +1,5 @@ using FluentAssertions; using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Testing; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/EventOrderScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/EventOrderScenario.cs index 5ac5766ed..79fd2633d 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/EventOrderScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/EventOrderScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/EventScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/EventScenario.cs index 7d5fd2dea..6c5439b18 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/EventScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/EventScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/FailingSagaScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/FailingSagaScenario.cs index 36c12c3bd..b2f83ef65 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/FailingSagaScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/FailingSagaScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs index f16c89b24..fb0747d5e 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ForkScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ForkScenario.cs index 54fb31432..89a9b46e0 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/ForkScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ForkScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/IfScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/IfScenario.cs index bd2c9808a..8b716715f 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/IfScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/IfScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs index afdbe588a..0f080504a 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/MultistepCompensationScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/NestedRetrySagaScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/NestedRetrySagaScenario.cs index dd4d2d720..f8b3c1a08 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/NestedRetrySagaScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/NestedRetrySagaScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ParallelScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ParallelScenario.cs index 12decbf73..f63041858 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/ParallelScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ParallelScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaScenario.cs index 70ce71510..c4ac48238 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/RetrySagaScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/SagaScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/SagaScenario.cs index bfa935ef7..de3dc1105 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/SagaScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/SagaScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs index eb51107b8..cbd04a40a 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; -using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; using FluentAssertions; -using WorkflowCore.Services.DefinitionStorage; using WorkflowCore.Testing; using WorkflowCore.TestAssets.DataTypes; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs index 26b1f687a..449a6c855 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; -using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; using FluentAssertions; -using WorkflowCore.Services.DefinitionStorage; using WorkflowCore.Testing; using WorkflowCore.TestAssets.DataTypes; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/UserScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/UserScenario.cs index b99ca99a8..66d442018 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/UserScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/UserScenario.cs @@ -1,12 +1,8 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; using FluentAssertions; -using FluentAssertions.Collections; -using WorkflowCore.Users.Models; using System.Linq; using WorkflowCore.Testing; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/WhenScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/WhenScenario.cs index 7d7095e6c..b56d4c81d 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/WhenScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/WhenScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/WhileScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/WhileScenario.cs index 5ca2cd20a..f91209d44 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/WhileScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/WhileScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using Xunit; diff --git a/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs b/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs index 091dafe22..cc9847574 100644 --- a/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs +++ b/test/WorkflowCore.IntegrationTests/SearchIndexTests.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using FluentAssertions; -using FluentAssertions.Collections; -using FluentAssertions.Equivalency; -using FluentAssertions.Common; using Xunit; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs b/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs index 3ef48c525..eec59ecd6 100644 --- a/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs +++ b/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace WorkflowCore.TestAssets.DataTypes { diff --git a/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs b/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs index 677fb511c..7ea5218bb 100644 --- a/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs +++ b/test/WorkflowCore.TestAssets/LockProvider/DistributedLockProviderTests.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; using WorkflowCore.Interface; diff --git a/test/WorkflowCore.TestAssets/Properties/AssemblyInfo.cs b/test/WorkflowCore.TestAssets/Properties/AssemblyInfo.cs index 31e55a72d..1fccce37d 100644 --- a/test/WorkflowCore.TestAssets/Properties/AssemblyInfo.cs +++ b/test/WorkflowCore.TestAssets/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/test/WorkflowCore.TestAssets/Steps/Counter.cs b/test/WorkflowCore.TestAssets/Steps/Counter.cs index af5b433f4..64d276199 100644 --- a/test/WorkflowCore.TestAssets/Steps/Counter.cs +++ b/test/WorkflowCore.TestAssets/Steps/Counter.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; diff --git a/test/WorkflowCore.TestAssets/Utils.cs b/test/WorkflowCore.TestAssets/Utils.cs index 48a479b47..723cca992 100644 --- a/test/WorkflowCore.TestAssets/Utils.cs +++ b/test/WorkflowCore.TestAssets/Utils.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Newtonsoft.Json; using System.IO; diff --git a/test/WorkflowCore.Testing/JsonWorkflowTest.cs b/test/WorkflowCore.Testing/JsonWorkflowTest.cs index cfabc7877..4e8f4281b 100644 --- a/test/WorkflowCore.Testing/JsonWorkflowTest.cs +++ b/test/WorkflowCore.Testing/JsonWorkflowTest.cs @@ -1,8 +1,6 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/test/WorkflowCore.Testing/WorkflowTest.cs index 3f8a29ea3..84db06002 100644 --- a/test/WorkflowCore.Testing/WorkflowTest.cs +++ b/test/WorkflowCore.Testing/WorkflowTest.cs @@ -1,8 +1,6 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; diff --git a/test/WorkflowCore.Testing/YamlWorkflowTest.cs b/test/WorkflowCore.Testing/YamlWorkflowTest.cs index a9d180b98..11b8e9d93 100644 --- a/test/WorkflowCore.Testing/YamlWorkflowTest.cs +++ b/test/WorkflowCore.Testing/YamlWorkflowTest.cs @@ -1,8 +1,6 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs b/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs index 6e52c238e..b9458fd1e 100644 --- a/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs +++ b/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs @@ -1,10 +1,5 @@ -using Docker.DotNet; -using Docker.DotNet.Models; -using System; -using System.Collections.Generic; +using System; using System.Net; -using System.Runtime.InteropServices; -using System.Text; using Docker.Testify; using Xunit; using Amazon.DynamoDBv2; diff --git a/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs index aa0663936..6fb9c1ea9 100644 --- a/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; using Microsoft.Extensions.Logging; using WorkflowCore.Interface; diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs index 66a621c4f..8b6754c02 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoBasicScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; -using Amazon.Runtime; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.DynamoDB; using Xunit; namespace WorkflowCore.Tests.DynamoDB.Scenarios diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs index 23ea7b32c..f475c58cb 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoCompensationScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; -using Amazon.Runtime; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.DynamoDB; using Xunit; namespace WorkflowCore.Tests.DynamoDB.Scenarios diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs index e0214203d..648b18868 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDataScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; -using Amazon.Runtime; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.DynamoDB; using Xunit; namespace WorkflowCore.Tests.DynamoDB.Scenarios diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs index d5d0ff5ac..419d7ac37 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoDynamicDataScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; -using Amazon.Runtime; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.DynamoDB; using Xunit; namespace WorkflowCore.Tests.DynamoDB.Scenarios diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs index 887e39541..e421d31a2 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoEventScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; -using Amazon.Runtime; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.DynamoDB; using Xunit; namespace WorkflowCore.Tests.DynamoDB.Scenarios diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs index a8026da4b..1c9e30679 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoForeachScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; -using Amazon.Runtime; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.DynamoDB; using Xunit; namespace WorkflowCore.Tests.DynamoDB.Scenarios diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs index 2b7ff3fac..efc3738d8 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoIfScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; -using Amazon.Runtime; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.DynamoDB; using Xunit; namespace WorkflowCore.Tests.DynamoDB.Scenarios diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs index a09dad420..5aea6ee1a 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoRetrySagaScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; -using Amazon.Runtime; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.DynamoDB; using Xunit; namespace WorkflowCore.Tests.DynamoDB.Scenarios diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs index 9cb309614..8c1f6a317 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoSagaScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; -using Amazon.Runtime; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.DynamoDB; using Xunit; namespace WorkflowCore.Tests.DynamoDB.Scenarios diff --git a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs index 50960652a..7912b3f53 100644 --- a/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs +++ b/test/WorkflowCore.Tests.DynamoDB/Scenarios/DynamoWhileScenario.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Amazon.DynamoDBv2; -using Amazon.Runtime; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.DynamoDB; using Xunit; namespace WorkflowCore.Tests.DynamoDB.Scenarios diff --git a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs index aef37beaa..4bb1c57f9 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs +++ b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Net; -using System.Text; using Docker.Testify; using Nest; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs index 515f3ed8a..9ce3ce7d7 100644 --- a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs +++ b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs @@ -1,9 +1,4 @@ -using Docker.DotNet; -using Docker.DotNet.Models; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; +using System; using Docker.Testify; using MongoDB.Driver; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/MongoPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.MongoDB/MongoPersistenceProviderFixture.cs index b3146105d..79de07a97 100644 --- a/test/WorkflowCore.Tests.MongoDB/MongoPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.MongoDB/MongoPersistenceProviderFixture.cs @@ -1,7 +1,5 @@ using MongoDB.Driver; using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Persistence.MongoDB.Services; using WorkflowCore.UnitTests; diff --git a/test/WorkflowCore.Tests.MongoDB/Properties/AssemblyInfo.cs b/test/WorkflowCore.Tests.MongoDB/Properties/AssemblyInfo.cs index 608b546e7..d533a178e 100644 --- a/test/WorkflowCore.Tests.MongoDB/Properties/AssemblyInfo.cs +++ b/test/WorkflowCore.Tests.MongoDB/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs index 29682a442..52625678f 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using MongoDB.Bson.Serialization; using WorkflowCore.IntegrationTests.Scenarios; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoBasicScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoBasicScenario.cs index 7ffad9531..bec7c6d50 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoBasicScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoBasicScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoCompensationScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoCompensationScenario.cs index 00fbec0c9..8adf4b4bd 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoCompensationScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoCompensationScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs index 520c62f5d..468915f75 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs index 828dda1a2..b5f8fd01f 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; -using MongoDB.Bson.Serialization; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoEventScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoEventScenario.cs index af2e51006..b789bab5b 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoEventScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoEventScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForEachScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForEachScenario.cs index 756e76595..c8b718753 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForEachScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForEachScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForkScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForkScenario.cs index 40c2ef556..319af0466 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForkScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForkScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoIfScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoIfScenario.cs index 51e7beee3..7ea42fd98 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoIfScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoIfScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoRetrySagaScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoRetrySagaScenario.cs index 311043260..b8a9ec92b 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoRetrySagaScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoRetrySagaScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoSagaScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoSagaScenario.cs index 1e5e384f9..e5d666a9c 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoSagaScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoSagaScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoUserScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoUserScenario.cs index c082624b8..93ec848ee 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoUserScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoUserScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhenScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhenScenario.cs index 8c8925e52..6cca92b6b 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhenScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhenScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhileScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhileScenario.cs index 7f2bdc43b..4c7aaead9 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhileScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhileScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs index 2b8dd3c9d..5ec59395d 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDynamicDataScenario.cs @@ -1,7 +1,6 @@ using System; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.MySQL; using Xunit; namespace WorkflowCore.Tests.MySQL.Scenarios diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs index 09838d64a..65e99ad84 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlEventScenario.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.MySQL; using Xunit; namespace WorkflowCore.Tests.MySQL.Scenarios diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs index 23111adbd..01a15de17 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForeachScenario.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.MySQL; using Xunit; namespace WorkflowCore.Tests.MySQL.Scenarios diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs index 0d2d43421..8a57e465b 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlForkScenario.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.MySQL; using Xunit; namespace WorkflowCore.Tests.MySQL.Scenarios diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs index d02f5dbe7..7cf6721fc 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlIfScenario.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.MySQL; using Xunit; namespace WorkflowCore.Tests.MySQL.Scenarios diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs index 12dcbeb58..f16190f73 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlRetrySagaScenario.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.MySQL; using Xunit; namespace WorkflowCore.Tests.MySQL.Scenarios diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs index 2d88ea835..e23719b22 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlSagaScenario.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.MySQL; using Xunit; namespace WorkflowCore.Tests.MySQL.Scenarios diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs index 22017da65..909b98b4f 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlUserScenario.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.MySQL; using Xunit; namespace WorkflowCore.Tests.MySQL.Scenarios diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs index dae355db2..e7721e382 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhenScenario.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.MySQL; using Xunit; namespace WorkflowCore.Tests.MySQL.Scenarios diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs index e6b07202e..8e6c5f6ee 100644 --- a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlWhileScenario.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; -using WorkflowCore.Tests.MySQL; using Xunit; namespace WorkflowCore.Tests.MySQL.Scenarios diff --git a/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs b/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs index c5e1d93b1..253493803 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs @@ -1,10 +1,5 @@ -using Docker.DotNet; -using Docker.DotNet.Models; -using System; +using System; using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; using Docker.Testify; using Npgsql; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs index 95ebe7976..9322b033a 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/PostgresPersistenceProviderFixture.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.PostgreSQL; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Properties/AssemblyInfo.cs b/test/WorkflowCore.Tests.PostgreSQL/Properties/AssemblyInfo.cs index 1d2a140ee..9d931e022 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Properties/AssemblyInfo.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresActivityScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresActivityScenario.cs index 7b63b42dd..2f31eca31 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresActivityScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresActivityScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresBasicScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresBasicScenario.cs index 7b7817e20..2b1a8e62d 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresBasicScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresBasicScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDataScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDataScenario.cs index e72fdcf07..3c0742b0b 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDataScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDataScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresEventScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresEventScenario.cs index 3af74377b..ec9d520cb 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresEventScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresEventScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresForeachScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresForeachScenario.cs index 49b2bd92b..a0c5e03f4 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresForeachScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresForeachScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresForkScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresForkScenario.cs index 9719ab4b9..4ad8fe86a 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresForkScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresForkScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresIfScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresIfScenario.cs index 68be367db..f962dd70d 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresIfScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresIfScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresRetrySagaScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresRetrySagaScenario.cs index f8c792aa6..0c63d54ff 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresRetrySagaScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresRetrySagaScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresSagaScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresSagaScenario.cs index 38f304029..2d42dcd10 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresSagaScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresSagaScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresUserScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresUserScenario.cs index 57c8fc5b9..5c371bb93 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresUserScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresUserScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresWhenScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresWhenScenario.cs index 9ffc43761..52445096a 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresWhenScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresWhenScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresWhileScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresWhileScenario.cs index 91532221a..9aaa2583c 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresWhileScenario.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresWhileScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs b/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs index cfd3d11f2..91fd3e32a 100644 --- a/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs +++ b/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs @@ -1,8 +1,4 @@ -using Docker.DotNet; -using Docker.DotNet.Models; -using System; -using System.Collections.Generic; -using System.Net; +using System; using Docker.Testify; using StackExchange.Redis; using Xunit; diff --git a/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs index cb68f7753..22a898e2b 100644 --- a/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.Redis/RedisPersistenceProviderFixture.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.Providers.Redis.Services; diff --git a/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs b/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs index 6b589666d..74aff9487 100644 --- a/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs +++ b/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs @@ -1,11 +1,6 @@ -using Docker.DotNet; -using Docker.DotNet.Models; -using System; +using System; using System.Collections.Generic; using System.Data.SqlClient; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; using Docker.Testify; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerActivityScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerActivityScenario.cs index cf09f78e3..2094cdf66 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerActivityScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerActivityScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerBasicScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerBasicScenario.cs index 1333131eb..56c951884 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerBasicScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerBasicScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerCompenstationScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerCompenstationScenario.cs index 2114b5d19..63b8ff470 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerCompenstationScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerCompenstationScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDataScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDataScenario.cs index e892534c4..0ecabc61f 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDataScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDataScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerEventScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerEventScenario.cs index 32cb07dc2..fce8a9bae 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerEventScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerEventScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerForEachScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerForEachScenario.cs index 2163fa998..b4df418d2 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerForEachScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerForEachScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerIfScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerIfScenario.cs index e51c4a514..798863285 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerIfScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerIfScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerRetrySagaScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerRetrySagaScenario.cs index 258ca298f..fd00b2d20 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerRetrySagaScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerRetrySagaScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerSagaScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerSagaScenario.cs index 2f1bf58ae..b8e13f0c4 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerSagaScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerSagaScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerWhenScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerWhenScenario.cs index 5180db8e9..77da4ee10 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerWhenScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerWhenScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerWhileScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerWhileScenario.cs index cd870f805..832579484 100644 --- a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerWhileScenario.cs +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerWhileScenario.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; diff --git a/test/WorkflowCore.Tests.Sqlite/SqliteCollection.cs b/test/WorkflowCore.Tests.Sqlite/SqliteCollection.cs index a57bc2c1b..69ed8c41a 100644 --- a/test/WorkflowCore.Tests.Sqlite/SqliteCollection.cs +++ b/test/WorkflowCore.Tests.Sqlite/SqliteCollection.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Xunit; namespace WorkflowCore.Tests.Sqlite diff --git a/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs b/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs index 5a27fd530..f8318ad9b 100644 --- a/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.Sqlite/SqlitePersistenceProviderFixture.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Persistence.EntityFramework.Services; using WorkflowCore.Persistence.Sqlite; diff --git a/test/WorkflowCore.UnitTests/Models/MemberMapParameterTests.cs b/test/WorkflowCore.UnitTests/Models/MemberMapParameterTests.cs index b486d039e..81e667139 100644 --- a/test/WorkflowCore.UnitTests/Models/MemberMapParameterTests.cs +++ b/test/WorkflowCore.UnitTests/Models/MemberMapParameterTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq.Expressions; using System.Threading.Tasks; using WorkflowCore.Interface; diff --git a/test/WorkflowCore.UnitTests/Properties/AssemblyInfo.cs b/test/WorkflowCore.UnitTests/Properties/AssemblyInfo.cs index a44538b0e..4e6557e23 100644 --- a/test/WorkflowCore.UnitTests/Properties/AssemblyInfo.cs +++ b/test/WorkflowCore.UnitTests/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs index 0a9293523..6176b6ee6 100644 --- a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs +++ b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs @@ -1,15 +1,11 @@ using FakeItEasy; using FluentAssertions; using System; -using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Services.DefinitionStorage; using WorkflowCore.TestAssets.DataTypes; -using WorkflowCore.TestAssets.Steps; using Xunit; namespace WorkflowCore.UnitTests.Services.DefinitionStorage diff --git a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs index f6bc0cf35..a5697882b 100644 --- a/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/ExecutionResultProcessorFixture.cs @@ -3,15 +3,12 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Services; using FluentAssertions; using Xunit; -using WorkflowCore.Primitives; using System.Linq.Expressions; -using System.Threading.Tasks; namespace WorkflowCore.UnitTests.Services { diff --git a/test/WorkflowCore.UnitTests/Services/MemoryPersistenceProviderFixture.cs b/test/WorkflowCore.UnitTests/Services/MemoryPersistenceProviderFixture.cs index f340f3010..1bfa2b3f7 100644 --- a/test/WorkflowCore.UnitTests/Services/MemoryPersistenceProviderFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/MemoryPersistenceProviderFixture.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Services; diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowRegistryFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowRegistryFixture.cs index c3dbb96fd..3fb0bb75d 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowRegistryFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowRegistryFixture.cs @@ -1,17 +1,9 @@ using FakeItEasy; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; -using System.Text; -using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Services; using FluentAssertions; using Xunit; -using WorkflowCore.Primitives; -using System.Linq.Expressions; -using System.Threading.Tasks; namespace WorkflowCore.UnitTests.Services { diff --git a/test/WorkflowCore.UnitTests/SingleNodeLockProviderTests/SingleNodeLockProviderTests.cs b/test/WorkflowCore.UnitTests/SingleNodeLockProviderTests/SingleNodeLockProviderTests.cs index d2cf041a5..f4f411ffb 100644 --- a/test/WorkflowCore.UnitTests/SingleNodeLockProviderTests/SingleNodeLockProviderTests.cs +++ b/test/WorkflowCore.UnitTests/SingleNodeLockProviderTests/SingleNodeLockProviderTests.cs @@ -1,8 +1,5 @@ -using FluentAssertions; -using NUnit.Framework; +using NUnit.Framework; using System; -using System.Collections.Generic; -using System.Text; using WorkflowCore.Interface; using WorkflowCore.Services; using WorkflowCore.TestAssets.LockProvider; From 106283b64987df3842fe1198083c9f6dedab3eab Mon Sep 17 00:00:00 2001 From: "divotchenko.gg" Date: Fri, 26 Mar 2021 09:55:06 +0300 Subject: [PATCH 303/462] Set CompleteTime to WorkflowInstance when terminating workflow. --- src/WorkflowCore/Models/WorkflowInstance.cs | 11 +++++------ .../Services/ErrorHandlers/TerminateHandler.cs | 14 ++++++++------ src/WorkflowCore/Services/WorkflowController.cs | 15 +++++++++------ src/WorkflowCore/Services/WorkflowExecutor.cs | 17 +++++++++-------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/WorkflowCore/Models/WorkflowInstance.cs b/src/WorkflowCore/Models/WorkflowInstance.cs index b75b284fc..71f1e3c28 100644 --- a/src/WorkflowCore/Models/WorkflowInstance.cs +++ b/src/WorkflowCore/Models/WorkflowInstance.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; namespace WorkflowCore.Models @@ -36,11 +35,11 @@ public bool IsBranchComplete(string parentId) } } - public enum WorkflowStatus + public enum WorkflowStatus { - Runnable = 0, - Suspended = 1, - Complete = 2, - Terminated = 3 + Runnable = 0, + Suspended = 1, + Complete = 2, + Terminated = 3, } } diff --git a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs index d89f7f470..b8862d0e0 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Text; + using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Models.LifeCycleEvents; @@ -10,21 +10,23 @@ namespace WorkflowCore.Services.ErrorHandlers public class TerminateHandler : IWorkflowErrorHandler { private readonly ILifeCycleEventPublisher _eventPublisher; - private readonly IDateTimeProvider _datetimeProvider; + private readonly IDateTimeProvider _dateTimeProvider; public WorkflowErrorHandling Type => WorkflowErrorHandling.Terminate; - public TerminateHandler(ILifeCycleEventPublisher eventPublisher, IDateTimeProvider datetimeProvider) + public TerminateHandler(ILifeCycleEventPublisher eventPublisher, IDateTimeProvider dateTimeProvider) { _eventPublisher = eventPublisher; - _datetimeProvider = datetimeProvider; + _dateTimeProvider = dateTimeProvider; } public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue bubbleUpQueue) { workflow.Status = WorkflowStatus.Terminated; - _eventPublisher.PublishNotification(new WorkflowTerminated() + workflow.CompleteTime = _dateTimeProvider.UtcNow; + + _eventPublisher.PublishNotification(new WorkflowTerminated { - EventTimeUtc = _datetimeProvider.UtcNow, + EventTimeUtc = _dateTimeProvider.UtcNow, Reference = workflow.Reference, WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 690a6a596..025346a10 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -1,8 +1,8 @@ using System; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; + using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using WorkflowCore.Exceptions; @@ -47,10 +47,10 @@ public Task StartWorkflow(string workflowId, int? version, object data = return StartWorkflow(workflowId, version, data, reference); } - public Task StartWorkflow(string workflowId, TData data = null, string reference=null) + public Task StartWorkflow(string workflowId, TData data = null, string reference = null) where TData : class, new() { - return StartWorkflow(workflowId, null, data, reference); + return StartWorkflow(workflowId, null, data, reference); } public async Task StartWorkflow(string workflowId, int? version, TData data = null, string reference=null) @@ -204,7 +204,10 @@ public async Task TerminateWorkflow(string workflowId) try { var wf = await _persistenceStore.GetWorkflowInstance(workflowId); + wf.Status = WorkflowStatus.Terminated; + wf.CompleteTime = _dateTimeProvider.UtcNow; + await _persistenceStore.PersistWorkflow(wf); await _queueProvider.QueueWork(workflowId, QueueType.Index); await _eventHub.PublishNotification(new WorkflowTerminated() @@ -226,7 +229,7 @@ await _eventHub.PublishNotification(new WorkflowTerminated() public void RegisterWorkflow() where TWorkflow : IWorkflow { - TWorkflow wf = ActivatorUtilities.CreateInstance(_serviceProvider); + var wf = ActivatorUtilities.CreateInstance(_serviceProvider); _registry.RegisterWorkflow(wf); } @@ -234,8 +237,8 @@ public void RegisterWorkflow() where TWorkflow : IWorkflow where TData : new() { - TWorkflow wf = ActivatorUtilities.CreateInstance(_serviceProvider); - _registry.RegisterWorkflow(wf); + var wf = ActivatorUtilities.CreateInstance(_serviceProvider); + _registry.RegisterWorkflow(wf); } } } diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index c6241fd64..30d3d91a9 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -1,9 +1,10 @@ -using Microsoft.Extensions.Logging; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; + +using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -62,7 +63,7 @@ public async Task Execute(WorkflowInstance workflow, Can { _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); pointer.SleepUntil = _datetimeProvider.UtcNow.Add(_options.ErrorRetryInterval); - wfResult.Errors.Add(new ExecutionError() + wfResult.Errors.Add(new ExecutionError { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, @@ -82,7 +83,7 @@ public async Task Execute(WorkflowInstance workflow, Can catch (Exception ex) { _logger.LogError(ex, "Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); - wfResult.Errors.Add(new ExecutionError() + wfResult.Errors.Add(new ExecutionError { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, @@ -116,7 +117,7 @@ private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, Workfl if (pointer.Status != PointerStatus.Running) { pointer.Status = PointerStatus.Running; - _publisher.PublishNotification(new StepStarted() + _publisher.PublishNotification(new StepStarted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, @@ -159,12 +160,12 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe { _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); pointer.SleepUntil = _datetimeProvider.UtcNow.Add(_options.ErrorRetryInterval); - wfResult.Errors.Add(new ExecutionError() + wfResult.Errors.Add(new ExecutionError { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.UtcNow, - Message = $"Unable to construct step body {step.BodyType.ToString()}" + Message = $"Unable to construct step body {step.BodyType}" }); return; } @@ -253,7 +254,7 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo await middlewareRunner.RunPostMiddleware(workflow, def); } - _publisher.PublishNotification(new WorkflowCompleted() + _publisher.PublishNotification(new WorkflowCompleted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, From e54fec2e4cf5cbee2f41f4ba9fd761ff13c9ab9a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 2 Apr 2021 11:05:47 -0700 Subject: [PATCH 304/462] fix registry --- src/WorkflowCore/Services/WorkflowRegistry.cs | 67 +++++++++++-------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowRegistry.cs b/src/WorkflowCore/Services/WorkflowRegistry.cs index 01d0625a5..beed19c0e 100644 --- a/src/WorkflowCore/Services/WorkflowRegistry.cs +++ b/src/WorkflowCore/Services/WorkflowRegistry.cs @@ -10,8 +10,9 @@ namespace WorkflowCore.Services { public class WorkflowRegistry : IWorkflowRegistry { - private readonly IServiceProvider _serviceProvider; - private readonly BlockingCollection<(string workflowId, int version, WorkflowDefinition definition)> _registry = new BlockingCollection<(string, int, WorkflowDefinition)>(); + private readonly IServiceProvider _serviceProvider; + private readonly ConcurrentDictionary _registry = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _lastestVersion = new ConcurrentDictionary(); public WorkflowRegistry(IServiceProvider serviceProvider) { @@ -20,75 +21,85 @@ public WorkflowRegistry(IServiceProvider serviceProvider) public WorkflowDefinition GetDefinition(string workflowId, int? version = null) { - (string workflowId, int version, WorkflowDefinition definition) workflowEntry; if (version.HasValue) { - workflowEntry = _registry.FirstOrDefault(x => x.workflowId == workflowId && x.version == version.Value); + if (!_registry.ContainsKey($"{workflowId}-{version}")) + return default; + return _registry[$"{workflowId}-{version}"]; } else { - workflowEntry = _registry.Where(x => x.workflowId == workflowId).OrderByDescending(x => x.version) - .FirstOrDefault(); + if (!_lastestVersion.ContainsKey(workflowId)) + return default; + return _lastestVersion[workflowId]; } - - return workflowEntry != default ? workflowEntry.definition : default; } public void DeregisterWorkflow(string workflowId, int version) { - var definition = _registry.FirstOrDefault(x => x.workflowId == workflowId && x.version == version); - if (definition != default) + if (!_registry.ContainsKey($"{workflowId}-{version}")) + return; + + lock (_registry) { - _registry.TryTake(out definition); + _registry.TryRemove($"{workflowId}-{version}", out var _); + if (_lastestVersion[workflowId].Version == version) + { + _lastestVersion.TryRemove(workflowId, out var _); + + var latest = _registry.Values.Where(x => x.Id == workflowId).OrderByDescending(x => x.Version).FirstOrDefault(); + if (latest != default) + _lastestVersion[workflowId] = latest; + } } } public void RegisterWorkflow(IWorkflow workflow) { - if (_registry.Any(x => x.workflowId == workflow.Id && x.version == workflow.Version)) - { - throw new InvalidOperationException($"Workflow {workflow.Id} version {workflow.Version} is already registered"); - } - var builder = _serviceProvider.GetService().UseData(); workflow.Build(builder); var def = builder.Build(workflow.Id, workflow.Version); - _registry.Add((workflow.Id, workflow.Version, def)); + RegisterWorkflow(def); } public void RegisterWorkflow(WorkflowDefinition definition) { - if (_registry.Any(x => x.workflowId == definition.Id && x.version == definition.Version)) + if (_registry.ContainsKey($"{definition.Id}-{definition.Version}")) { throw new InvalidOperationException($"Workflow {definition.Id} version {definition.Version} is already registered"); } - _registry.Add((definition.Id, definition.Version, definition)); + lock (_registry) + { + _registry[$"{definition.Id}-{definition.Version}"] = definition; + if (!_lastestVersion.ContainsKey(definition.Id)) + { + _lastestVersion[definition.Id] = definition; + return; + } + + if (_lastestVersion[definition.Id].Version <= definition.Version) + _lastestVersion[definition.Id] = definition; + } } public void RegisterWorkflow(IWorkflow workflow) where TData : new() { - if (_registry.Any(x => x.workflowId == workflow.Id && x.version == workflow.Version)) - { - throw new InvalidOperationException($"Workflow {workflow.Id} version {workflow.Version} is already registered"); - } - var builder = _serviceProvider.GetService().UseData(); workflow.Build(builder); var def = builder.Build(workflow.Id, workflow.Version); - _registry.Add((workflow.Id, workflow.Version, def)); + RegisterWorkflow(def); } public bool IsRegistered(string workflowId, int version) { - var definition = _registry.FirstOrDefault(x => x.workflowId == workflowId && x.version == version); - return definition != default; + return _registry.ContainsKey($"{workflowId}-{version}"); } public IEnumerable GetAllDefinitions() { - return _registry.Select(i => i.definition); + return _registry.Values; } } } \ No newline at end of file From 97ba822fda48d4f2ed59cac155f35e33c12d2b91 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 2 Apr 2021 11:12:45 -0700 Subject: [PATCH 305/462] fix test constructor --- src/samples/WorkflowCore.TestSample01/NUnitTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/samples/WorkflowCore.TestSample01/NUnitTest.cs b/src/samples/WorkflowCore.TestSample01/NUnitTest.cs index 2b2b898e6..91be04202 100644 --- a/src/samples/WorkflowCore.TestSample01/NUnitTest.cs +++ b/src/samples/WorkflowCore.TestSample01/NUnitTest.cs @@ -11,9 +11,9 @@ namespace WorkflowCore.TestSample01 public class NUnitTest : WorkflowTest { [SetUp] - protected override void Setup(bool registerClassMap = false) + protected void Setup() { - base.Setup(registerClassMap); + base.Setup(false); } [Test] From 0c9085c504d2b2907fa08de5866bcc988f23d8ef Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 2 Apr 2021 11:13:26 -0700 Subject: [PATCH 306/462] bump version --- src/WorkflowCore/WorkflowCore.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 2f465db0d..2ef5a4a1c 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.5 - 3.3.5.0 - 3.3.5.0 + 3.3.6 + 3.3.6.0 + 3.3.6.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.5 + 3.3.6 From 00f790d77237445182e8fab11c612f1acf319439 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 5 Apr 2021 08:44:24 -0700 Subject: [PATCH 307/462] pr templates --- .github/PULL_REQUEST_TEMPLATE/bug_fix.md | 33 ++++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE/new_feature.md | 23 ++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/bug_fix.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/new_feature.md diff --git a/.github/PULL_REQUEST_TEMPLATE/bug_fix.md b/.github/PULL_REQUEST_TEMPLATE/bug_fix.md new file mode 100644 index 000000000..0147a0895 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/bug_fix.md @@ -0,0 +1,33 @@ +--- +name: Bug Fix +about: Fixing an issue with existing functionality. +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Describe the change** +A clear and concise description of what the change is. + +**Describe your implementation or design** +How did you go about implementating the change? + +**Tests** +Did you cover your changes with tests? + +**Breaking change** +Do you changes break compatibility with previous versions? + + +**Additonal context** +Any additonal information you'd like to provide? diff --git a/.github/PULL_REQUEST_TEMPLATE/new_feature.md b/.github/PULL_REQUEST_TEMPLATE/new_feature.md new file mode 100644 index 000000000..cee9ea093 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/new_feature.md @@ -0,0 +1,23 @@ +--- +name: New Fuctionality +about: Adding new functionality to enable more use cases +title: '' +labels: '' +assignees: '' + +--- + +**Describe the change** +A clear and concise description of what the change is. + +**Describe your implementation or design** +How did you go about implementating the change? + +**Tests** +Did you cover your changes with tests? + +**Breaking change** +Do you changes break compatibility with previous versions? + +**Additonal context** +Any additonal information you'd like to provide? \ No newline at end of file From 1cd2168cfe3870a6185461712ce852e3a11370a7 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 5 Apr 2021 08:52:58 -0700 Subject: [PATCH 308/462] pr template --- .github/PULL_REQUEST_TEMPLATE/bug_fix.md | 33 ------------------- ...ew_feature.md => pull_request_template.md} | 10 +----- 2 files changed, 1 insertion(+), 42 deletions(-) delete mode 100644 .github/PULL_REQUEST_TEMPLATE/bug_fix.md rename .github/{PULL_REQUEST_TEMPLATE/new_feature.md => pull_request_template.md} (63%) diff --git a/.github/PULL_REQUEST_TEMPLATE/bug_fix.md b/.github/PULL_REQUEST_TEMPLATE/bug_fix.md deleted file mode 100644 index 0147a0895..000000000 --- a/.github/PULL_REQUEST_TEMPLATE/bug_fix.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -name: Bug Fix -about: Fixing an issue with existing functionality. -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Describe the change** -A clear and concise description of what the change is. - -**Describe your implementation or design** -How did you go about implementating the change? - -**Tests** -Did you cover your changes with tests? - -**Breaking change** -Do you changes break compatibility with previous versions? - - -**Additonal context** -Any additonal information you'd like to provide? diff --git a/.github/PULL_REQUEST_TEMPLATE/new_feature.md b/.github/pull_request_template.md similarity index 63% rename from .github/PULL_REQUEST_TEMPLATE/new_feature.md rename to .github/pull_request_template.md index cee9ea093..9c41f2455 100644 --- a/.github/PULL_REQUEST_TEMPLATE/new_feature.md +++ b/.github/pull_request_template.md @@ -1,14 +1,6 @@ ---- -name: New Fuctionality -about: Adding new functionality to enable more use cases -title: '' -labels: '' -assignees: '' - ---- **Describe the change** -A clear and concise description of what the change is. +A clear and concise description of what the change is. Any PR submitted without a description of the change will not be reviewed. **Describe your implementation or design** How did you go about implementating the change? From 30c98f83c5d70bae11dd2b65bada0db5619340f3 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 5 Apr 2021 08:54:19 -0700 Subject: [PATCH 309/462] spelling --- .github/pull_request_template.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 9c41f2455..30d77c8db 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,7 +3,7 @@ A clear and concise description of what the change is. Any PR submitted without a description of the change will not be reviewed. **Describe your implementation or design** -How did you go about implementating the change? +How did you go about implementing the change? **Tests** Did you cover your changes with tests? @@ -11,5 +11,5 @@ Did you cover your changes with tests? **Breaking change** Do you changes break compatibility with previous versions? -**Additonal context** -Any additonal information you'd like to provide? \ No newline at end of file +**Additional context** +Any additional information you'd like to provide? \ No newline at end of file From 5a44e400447b34dd1ace141e5b7d06595dd628e3 Mon Sep 17 00:00:00 2001 From: Bruce Zlata Date: Thu, 15 Apr 2021 15:41:41 +0800 Subject: [PATCH 310/462] fix cosmos client leak # Add CosmosDbClient which implements a interface for DI # Use singleton for CosmosDbClient and it will be disposed by dotnet container # Fix EnsureStoreExists deaklock risk --- .../Interface/ICosmosDbClient.cs | 9 ++++ .../ServiceCollectionExtensions.cs | 5 +- .../Services/CosmosDbClient.cs | 48 +++++++++++++++++++ .../Services/CosmosDbPersistenceProvider.cs | 14 +++--- .../Services/CosmosDbProvisioner.cs | 8 ++-- 5 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs create mode 100644 src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs diff --git a/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs b/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs new file mode 100644 index 000000000..bf5c2275c --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs @@ -0,0 +1,9 @@ +using Microsoft.Azure.Cosmos; + +namespace WorkflowCore.Providers.Azure.Interface +{ + public interface ICosmosDbClient + { + CosmosClient GetCosmosClient(); + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs index 9129e4de5..bdfd29d1e 100644 --- a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs @@ -28,8 +28,9 @@ public static WorkflowOptions UseAzureServiceBusEventHub( public static WorkflowOptions UseCosmosDbPersistence(this WorkflowOptions options, string connectionString, string databaseId) { - options.Services.AddTransient(sp => new CosmosDbProvisioner(connectionString, sp.GetService())); - options.UsePersistence(sp => new CosmosDbPersistenceProvider(connectionString, databaseId, sp.GetService())); + options.Services.AddSingleton(sp => new CosmosDbClient(connectionString)); + options.Services.AddTransient(sp => new CosmosDbProvisioner(sp.GetService(), sp.GetService())); + options.UsePersistence(sp => new CosmosDbPersistenceProvider(sp.GetService(), databaseId, sp.GetService())); return options; } } diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs new file mode 100644 index 000000000..cda487e14 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.Azure.Cosmos; +using WorkflowCore.Providers.Azure.Interface; + +namespace WorkflowCore.Providers.Azure.Services +{ + public class CosmosDbClient : ICosmosDbClient, IDisposable + { + private bool isDisposed = false; + + public CosmosClient _client; + + public CosmosDbClient(string connectionString) + { + _client = new CosmosClient(connectionString); + } + + public CosmosClient GetCosmosClient() + { + return this._client; + } + + /// + /// Dispose of cosmos client + /// + public void Dispose() + { + this.Dispose(true); + } + + /// + /// Dispose of cosmos client + /// + /// True if disposing + protected virtual void Dispose(bool disposing) + { + if (!this.isDisposed) + { + if (disposing) + { + this._client.Dispose(); + } + + this.isDisposed = true; + } + } + } +} \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs index a30ca8f27..e3fbc97f0 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs @@ -19,19 +19,19 @@ public class CosmosDbPersistenceProvider : IPersistenceProvider private ICosmosDbProvisioner _provisioner; private string _dbId; - private CosmosClient _client; + private ICosmosDbClient _client; private Lazy _workflowContainer; private Lazy _eventContainer; private Lazy _subscriptionContainer; - public CosmosDbPersistenceProvider(string connectionString, string dbId, ICosmosDbProvisioner provisioner) + public CosmosDbPersistenceProvider(ICosmosDbClient client, string dbId, ICosmosDbProvisioner provisioner) { _provisioner = provisioner; _dbId = dbId; - _client = new CosmosClient(connectionString); - _workflowContainer = new Lazy(() => _client.GetDatabase(_dbId).GetContainer(WorkflowContainerName)); - _eventContainer = new Lazy(() => _client.GetDatabase(_dbId).GetContainer(EventContainerName)); - _subscriptionContainer = new Lazy(() => _client.GetDatabase(_dbId).GetContainer(SubscriptionContainerName)); + _client = client; + _workflowContainer = new Lazy(() => _client.GetCosmosClient().GetDatabase(_dbId).GetContainer(WorkflowContainerName)); + _eventContainer = new Lazy(() => _client.GetCosmosClient().GetDatabase(_dbId).GetContainer(EventContainerName)); + _subscriptionContainer = new Lazy(() => _client.GetCosmosClient().GetDatabase(_dbId).GetContainer(SubscriptionContainerName)); } public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) @@ -70,7 +70,7 @@ public async Task CreateNewWorkflow(WorkflowInstance workflow) public void EnsureStoreExists() { - _provisioner.Provision(_dbId).Wait(); + _provisioner.Provision(_dbId).ConfigureAwait(false).GetAwaiter().GetResult(); } public async Task GetEvent(string id) diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs index ab86cc3f6..359ef799e 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs @@ -9,16 +9,16 @@ namespace WorkflowCore.Providers.Azure.Services public class CosmosDbProvisioner : ICosmosDbProvisioner { - private CosmosClient _client; + private ICosmosDbClient _client; - public CosmosDbProvisioner(string connectionString, ILoggerFactory loggerFactory) + public CosmosDbProvisioner(ICosmosDbClient client, ILoggerFactory loggerFactory) { - _client = new CosmosClient(connectionString); + _client = client; } public async Task Provision(string dbId) { - var dbResp = await _client.CreateDatabaseIfNotExistsAsync(dbId); + var dbResp = await _client.GetCosmosClient().CreateDatabaseIfNotExistsAsync(dbId); var wfIndexPolicy = new IndexingPolicy(); wfIndexPolicy.IncludedPaths.Add(new IncludedPath { Path = @"/*" }); wfIndexPolicy.ExcludedPaths.Add(new ExcludedPath { Path = @"/ExecutionPointers/?" }); From 98df2bbac23c0b69934c8666269e77adb88b9ea9 Mon Sep 17 00:00:00 2001 From: glucaci Date: Thu, 15 Apr 2021 23:41:57 +0300 Subject: [PATCH 311/462] Add new WorkflowMiddlewarePhase --- .../Interface/IWorkflowMiddleware.cs | 7 +++- .../Interface/IWorkflowMiddlewareRunner.cs | 13 ++++++- src/WorkflowCore/Services/WorkflowExecutor.cs | 12 +++++-- .../Services/WorkflowMiddlewareRunner.cs | 35 ++++++------------- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/WorkflowCore/Interface/IWorkflowMiddleware.cs b/src/WorkflowCore/Interface/IWorkflowMiddleware.cs index 71781b30d..ede4ca8ec 100644 --- a/src/WorkflowCore/Interface/IWorkflowMiddleware.cs +++ b/src/WorkflowCore/Interface/IWorkflowMiddleware.cs @@ -16,7 +16,12 @@ public enum WorkflowMiddlewarePhase /// /// The middleware should run after a workflow completes. /// - PostWorkflow + PostWorkflow, + + /// + /// The middleware should run after each workflow execution. + /// + ExecuteWorkflow } /// diff --git a/src/WorkflowCore/Interface/IWorkflowMiddlewareRunner.cs b/src/WorkflowCore/Interface/IWorkflowMiddlewareRunner.cs index 96aab8b74..6c47899f5 100644 --- a/src/WorkflowCore/Interface/IWorkflowMiddlewareRunner.cs +++ b/src/WorkflowCore/Interface/IWorkflowMiddlewareRunner.cs @@ -4,7 +4,7 @@ namespace WorkflowCore.Interface { /// - /// Runs workflow pre/post middleware. + /// Runs workflow pre/post and execute middleware. /// public interface IWorkflowMiddlewareRunner { @@ -29,5 +29,16 @@ public interface IWorkflowMiddlewareRunner /// The definition. /// A task that will complete when all middleware has run. Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinition def); + + /// + /// Runs workflow-level middleware that is set to run at the + /// phase. Middleware will be run in the + /// order in which they were registered with DI with middleware declared earlier starting earlier and + /// completing later. + /// + /// The to run for. + /// The definition. + /// A task that will complete when all middleware has run. + Task RunExecuteMiddleware(WorkflowInstance workflow, WorkflowDefinition def); } } diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index ee58e32bb..5c72355a4 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -96,12 +96,12 @@ public async Task Execute(WorkflowInstance workflow, Can _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); } ProcessAfterExecutionIteration(workflow, def, wfResult); - DetermineNextExecutionTime(workflow, def); + await DetermineNextExecutionTime(workflow, def); using (var scope = _serviceProvider.CreateScope()) { var middlewareRunner = scope.ServiceProvider.GetRequiredService(); - await middlewareRunner.RunPostMiddleware(workflow, def); + await middlewareRunner.RunExecuteMiddleware(workflow, def); } return wfResult; @@ -212,7 +212,7 @@ private void ProcessAfterExecutionIteration(WorkflowInstance workflow, WorkflowD } } - private void DetermineNextExecutionTime(WorkflowInstance workflow, WorkflowDefinition def) + private async Task DetermineNextExecutionTime(WorkflowInstance workflow, WorkflowDefinition def) { //TODO: move to own class workflow.NextExecution = null; @@ -257,6 +257,12 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow, WorkflowDefin workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.UtcNow; + using (var scope = _serviceProvider.CreateScope()) + { + var middlewareRunner = scope.ServiceProvider.GetRequiredService(); + await middlewareRunner.RunPostMiddleware(workflow, def); + } + _publisher.PublishNotification(new WorkflowCompleted { EventTimeUtc = _datetimeProvider.UtcNow, diff --git a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs index 6eb6968b8..9b6eea445 100644 --- a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs +++ b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs @@ -8,7 +8,7 @@ namespace WorkflowCore.Services { - /// + /// public class WorkflowMiddlewareRunner : IWorkflowMiddlewareRunner { private static readonly WorkflowDelegate NoopWorkflowDelegate = () => Task.CompletedTask; @@ -17,23 +17,13 @@ public class WorkflowMiddlewareRunner : IWorkflowMiddlewareRunner public WorkflowMiddlewareRunner( IEnumerable middleware, - IServiceProvider serviceProvider - ) + IServiceProvider serviceProvider) { _middleware = middleware; _serviceProvider = serviceProvider; } - - /// - /// Runs workflow-level middleware that is set to run at the - /// phase. Middleware will be run in the - /// order in which they were registered with DI with middleware declared earlier starting earlier and - /// completing later. - /// - /// The to run for. - /// The definition. - /// A task that will complete when all middleware has run. + /// public async Task RunPreMiddleware(WorkflowInstance workflow, WorkflowDefinition def) { var preMiddleware = _middleware @@ -43,15 +33,7 @@ public async Task RunPreMiddleware(WorkflowInstance workflow, WorkflowDefinition await RunWorkflowMiddleware(workflow, preMiddleware); } - /// - /// Runs workflow-level middleware that is set to run at the - /// phase. Middleware will be run in the - /// order in which they were registered with DI with middleware declared earlier starting earlier and - /// completing later. - /// - /// The to run for. - /// The definition. - /// A task that will complete when all middleware has run. + /// public async Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinition def) { var postMiddleware = _middleware @@ -77,10 +59,15 @@ public async Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinitio } } + /// + public Task RunExecuteMiddleware(WorkflowInstance workflow, WorkflowDefinition def) + { + throw new NotImplementedException(); + } + private static async Task RunWorkflowMiddleware( WorkflowInstance workflow, - IEnumerable middlewareCollection - ) + IEnumerable middlewareCollection) { // Build the middleware chain var middlewareChain = middlewareCollection From 076d1b8dfbe25f8421f6642f45b1da86ae5927ee Mon Sep 17 00:00:00 2001 From: glucaci Date: Fri, 16 Apr 2021 00:03:27 +0300 Subject: [PATCH 312/462] Add execute middleware implementation --- .../Services/WorkflowMiddlewareRunner.cs | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs index 9b6eea445..d22470412 100644 --- a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs +++ b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs @@ -27,8 +27,7 @@ public WorkflowMiddlewareRunner( public async Task RunPreMiddleware(WorkflowInstance workflow, WorkflowDefinition def) { var preMiddleware = _middleware - .Where(m => m.Phase == WorkflowMiddlewarePhase.PreWorkflow) - .ToArray(); + .Where(m => m.Phase == WorkflowMiddlewarePhase.PreWorkflow); await RunWorkflowMiddleware(workflow, preMiddleware); } @@ -37,47 +36,58 @@ public async Task RunPreMiddleware(WorkflowInstance workflow, WorkflowDefinition public async Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinition def) { var postMiddleware = _middleware - .Where(m => m.Phase == WorkflowMiddlewarePhase.PostWorkflow) - .ToArray(); - + .Where(m => m.Phase == WorkflowMiddlewarePhase.PostWorkflow); try { await RunWorkflowMiddleware(workflow, postMiddleware); } catch (Exception exception) { - // On error, determine which error handler to run and then run it + // TODO: + // OnPostMiddlewareError should be IWorkflowMiddlewareErrorHandler + // because we don't know to run other error handler type var errorHandlerType = def.OnPostMiddlewareError ?? typeof(IWorkflowMiddlewareErrorHandler); - using (var scope = _serviceProvider.CreateScope()) - { - var typeInstance = scope.ServiceProvider.GetService(errorHandlerType); - if (typeInstance != null && typeInstance is IWorkflowMiddlewareErrorHandler handler) - { - await handler.HandleAsync(exception); - } - } + await HandleWorkflowMiddlewareError(exception); } } /// - public Task RunExecuteMiddleware(WorkflowInstance workflow, WorkflowDefinition def) + public async Task RunExecuteMiddleware(WorkflowInstance workflow, WorkflowDefinition def) + { + var executeMiddleware = _middleware + .Where(m => m.Phase == WorkflowMiddlewarePhase.ExecuteWorkflow); + + try + { + await RunWorkflowMiddleware(workflow, executeMiddleware); + } + catch (Exception exception) + { + await HandleWorkflowMiddlewareError(exception); + } + } + + private async Task HandleWorkflowMiddlewareError(Exception exception) { - throw new NotImplementedException(); + using (var scope = _serviceProvider.CreateScope()) + { + var handler = scope.ServiceProvider.GetService(); + if (handler != null) + { + await handler.HandleAsync(exception); + } + } } - private static async Task RunWorkflowMiddleware( + private static Task RunWorkflowMiddleware( WorkflowInstance workflow, IEnumerable middlewareCollection) { - // Build the middleware chain - var middlewareChain = middlewareCollection + return middlewareCollection .Reverse() - .Aggregate( - NoopWorkflowDelegate, - (previous, middleware) => () => middleware.HandleAsync(workflow, previous) - ); - - await middlewareChain(); + .Aggregate(NoopWorkflowDelegate, + (previous, middleware) => + () => middleware.HandleAsync(workflow, previous))(); } } } From 2f28bd5b000230b4da0e5e22a09da24dbfe63556 Mon Sep 17 00:00:00 2001 From: glucaci Date: Fri, 16 Apr 2021 00:20:13 +0300 Subject: [PATCH 313/462] Add custom error handler for execute workflow middleware and tests --- src/WorkflowCore/Models/WorkflowDefinition.cs | 1 + .../Services/WorkflowMiddlewareRunner.cs | 34 +++--- .../Services/WorkflowMiddlewareRunnerTests.cs | 114 ++++++++++++++++++ 3 files changed, 131 insertions(+), 18 deletions(-) diff --git a/src/WorkflowCore/Models/WorkflowDefinition.cs b/src/WorkflowCore/Models/WorkflowDefinition.cs index 0cea6cc40..d40fc5a2e 100644 --- a/src/WorkflowCore/Models/WorkflowDefinition.cs +++ b/src/WorkflowCore/Models/WorkflowDefinition.cs @@ -18,6 +18,7 @@ public class WorkflowDefinition public WorkflowErrorHandling DefaultErrorBehavior { get; set; } public Type OnPostMiddlewareError { get; set; } + public Type OnExecuteMiddlewareError { get; set; } public TimeSpan? DefaultErrorRetryInterval { get; set; } diff --git a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs index d22470412..872721849 100644 --- a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs +++ b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs @@ -43,11 +43,7 @@ public async Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinitio } catch (Exception exception) { - // TODO: - // OnPostMiddlewareError should be IWorkflowMiddlewareErrorHandler - // because we don't know to run other error handler type - var errorHandlerType = def.OnPostMiddlewareError ?? typeof(IWorkflowMiddlewareErrorHandler); - await HandleWorkflowMiddlewareError(exception); + await HandleWorkflowMiddlewareError(def.OnPostMiddlewareError, exception); } } @@ -63,19 +59,7 @@ public async Task RunExecuteMiddleware(WorkflowInstance workflow, WorkflowDefini } catch (Exception exception) { - await HandleWorkflowMiddlewareError(exception); - } - } - - private async Task HandleWorkflowMiddlewareError(Exception exception) - { - using (var scope = _serviceProvider.CreateScope()) - { - var handler = scope.ServiceProvider.GetService(); - if (handler != null) - { - await handler.HandleAsync(exception); - } + await HandleWorkflowMiddlewareError(def.OnExecuteMiddlewareError, exception); } } @@ -89,5 +73,19 @@ private static Task RunWorkflowMiddleware( (previous, middleware) => () => middleware.HandleAsync(workflow, previous))(); } + + private async Task HandleWorkflowMiddlewareError(Type middlewareErrorType, Exception exception) + { + var errorHandlerType = middlewareErrorType ?? typeof(IWorkflowMiddlewareErrorHandler); + + using (var scope = _serviceProvider.CreateScope()) + { + var typeInstance = scope.ServiceProvider.GetService(errorHandlerType); + if (typeInstance is IWorkflowMiddlewareErrorHandler handler) + { + await handler.HandleAsync(exception); + } + } + } } } diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs b/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs index 991acfb23..0e9c0da07 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs @@ -238,6 +238,120 @@ public async Task .MustHaveHappenedOnceExactly(); } + [Fact(DisplayName = "RunExecuteMiddleware should run nothing when no middleware")] + public void RunExecuteMiddleware_should_run_nothing_when_no_middleware() + { + // Act + Func action = async () => await Runner.RunExecuteMiddleware(Workflow, Definition); + + // Assert + action.ShouldNotThrow(); + } + + [Fact(DisplayName = "RunExecuteMiddleware should run middleware when one middleware")] + public async Task RunExecuteMiddleware_should_run_middleware_when_one_middleware() + { + // Arrange + var middleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow); + Middleware.Add(middleware); + + // Act + await Runner.RunExecuteMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(middleware)) + .MustHaveHappenedOnceExactly(); + } + + [Fact(DisplayName = "RunExecuteMiddleware should run all middleware when multiple middleware")] + public async Task RunExecuteMiddleware_should_run_all_middleware_when_multiple_middleware() + { + // Arrange + var middleware1 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 1); + var middleware2 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 2); + var middleware3 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 3); + Middleware.AddRange(new[] { middleware1, middleware2, middleware3 }); + + // Act + await Runner.RunPostMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(middleware3)) + .MustHaveHappenedOnceExactly() + .Then(A + .CallTo(HandleMethodFor(middleware2)) + .MustHaveHappenedOnceExactly()) + .Then(A + .CallTo(HandleMethodFor(middleware1)) + .MustHaveHappenedOnceExactly()); + } + + [Fact(DisplayName = "RunExecuteMiddleware should run middleware in ExecuteWorkflow and PostWorkflow phase")] + public async Task RunExecuteMiddleware_should_run_middleware_in_ExecuteWorkflow_and_PostWorkflow_phase() + { + // Arrange + var executeMiddleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 1); + var postMiddleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 2); + var preMiddleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow, 3); + Middleware.AddRange(new[] { preMiddleware, postMiddleware, executeMiddleware }); + + // Act + // TODO: add same test when workflow not completed + await Runner.RunExecuteMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(executeMiddleware)) + .MustHaveHappenedOnceExactly() + .Then(A + .CallTo(HandleMethodFor(postMiddleware)) + .MustHaveHappenedOnceExactly()); + + A.CallTo(HandleMethodFor(preMiddleware)).MustNotHaveHappened(); + } + + [Fact(DisplayName = "RunExecuteMiddleware should call top level error handler when middleware throws")] + public async Task RunExecuteMiddleware_should_call_top_level_error_handler_when_middleware_throws() + { + // Arrange + var middleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 1); + A.CallTo(HandleMethodFor(middleware)).ThrowsAsync(new ApplicationException("Something went wrong")); + Middleware.AddRange(new[] { middleware }); + + // Act + await Runner.RunExecuteMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(TopLevelErrorHandler)) + .MustHaveHappenedOnceExactly(); + } + + [Fact(DisplayName = + "RunExecuteMiddleware should call error handler on workflow def when middleware throws and def has handler defined")] + public async Task + RunExecuteMiddleware_should_call_error_handler_on_workflow_def_when_middleware_throws_and_def_has_handler() + { + // Arrange + var middleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 1); + A.CallTo(HandleMethodFor(middleware)).ThrowsAsync(new ApplicationException("Something went wrong")); + Middleware.AddRange(new[] { middleware }); + Definition.OnExecuteMiddlewareError = typeof(IDefLevelErrorHandler); + + // Act + await Runner.RunExecuteMiddleware(Workflow, Definition); + + // Assert + A + .CallTo(HandleMethodFor(TopLevelErrorHandler)) + .MustNotHaveHappened(); + A + .CallTo(HandleMethodFor(DefLevelErrorHandler)) + .MustHaveHappenedOnceExactly(); + } + #region Helpers private IWorkflowMiddleware BuildWorkflowMiddleware( From ae7d64f150e84f0755ecbc8cadaa842fcda78a6a Mon Sep 17 00:00:00 2001 From: glucaci Date: Fri, 16 Apr 2021 00:21:58 +0300 Subject: [PATCH 314/462] Add tests --- .../Services/WorkflowMiddlewareRunnerTests.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs b/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs index 0e9c0da07..4b58d965f 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs @@ -274,7 +274,7 @@ public async Task RunExecuteMiddleware_should_run_all_middleware_when_multiple_m Middleware.AddRange(new[] { middleware1, middleware2, middleware3 }); // Act - await Runner.RunPostMiddleware(Workflow, Definition); + await Runner.RunExecuteMiddleware(Workflow, Definition); // Assert A @@ -288,8 +288,8 @@ public async Task RunExecuteMiddleware_should_run_all_middleware_when_multiple_m .MustHaveHappenedOnceExactly()); } - [Fact(DisplayName = "RunExecuteMiddleware should run middleware in ExecuteWorkflow and PostWorkflow phase")] - public async Task RunExecuteMiddleware_should_run_middleware_in_ExecuteWorkflow_and_PostWorkflow_phase() + [Fact(DisplayName = "RunExecuteMiddleware should only run middleware in ExecuteWorkflow phase")] + public async Task RunExecuteMiddleware_should_only_run_middleware_in_ExecuteWorkflow_phase() { // Arrange var executeMiddleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 1); @@ -298,18 +298,15 @@ public async Task RunExecuteMiddleware_should_run_middleware_in_ExecuteWorkflow_ Middleware.AddRange(new[] { preMiddleware, postMiddleware, executeMiddleware }); // Act - // TODO: add same test when workflow not completed await Runner.RunExecuteMiddleware(Workflow, Definition); // Assert A .CallTo(HandleMethodFor(executeMiddleware)) - .MustHaveHappenedOnceExactly() - .Then(A - .CallTo(HandleMethodFor(postMiddleware)) - .MustHaveHappenedOnceExactly()); + .MustHaveHappenedOnceExactly(); A.CallTo(HandleMethodFor(preMiddleware)).MustNotHaveHappened(); + A.CallTo(HandleMethodFor(postMiddleware)).MustNotHaveHappened(); } [Fact(DisplayName = "RunExecuteMiddleware should call top level error handler when middleware throws")] From 0239cd3a76cf0b71f13adb335a3c6f28ccf96b1b Mon Sep 17 00:00:00 2001 From: glucaci Date: Fri, 16 Apr 2021 00:40:35 +0300 Subject: [PATCH 315/462] Add two more tests --- .../Services/WorkflowExecutorFixture.cs | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs index 037dd0e33..e548b8e88 100644 --- a/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs +++ b/test/WorkflowCore.UnitTests/Services/WorkflowExecutorFixture.cs @@ -44,8 +44,14 @@ public WorkflowExecutorFixture() Options = new WorkflowOptions(A.Fake()); + var stepExecutionScope = A.Fake(); + A.CallTo(() => ScopeProvider.CreateScope(A._)).Returns(stepExecutionScope); + A.CallTo(() => stepExecutionScope.ServiceProvider).Returns(ServiceProvider); + var scope = A.Fake(); - A.CallTo(() => ScopeProvider.CreateScope(A._)).Returns(scope); + var scopeFactory = A.Fake(); + A.CallTo(() => ServiceProvider.GetService(typeof(IServiceScopeFactory))).Returns(scopeFactory); + A.CallTo(() => scopeFactory.CreateScope()).Returns(scope); A.CallTo(() => scope.ServiceProvider).Returns(ServiceProvider); A.CallTo(() => DateTimeProvider.Now).Returns(DateTime.Now); @@ -63,6 +69,10 @@ public WorkflowExecutorFixture() .RunPostMiddleware(A._, A._)) .Returns(Task.CompletedTask); + A.CallTo(() => MiddlewareRunner + .RunExecuteMiddleware(A._, A._)) + .Returns(Task.CompletedTask); + A.CallTo(() => StepExecutor.ExecuteStep(A._, A._)) .ReturnsLazily(call => call.Arguments[1].As().RunAsync( @@ -105,6 +115,64 @@ public void should_execute_active_step() A.CallTo(() => ResultProcesser.ProcessExecutionResult(instance, A.Ignored, A.Ignored, step1, A.Ignored, A.Ignored)).MustHaveHappened(); } + [Fact(DisplayName = "Should call execute middleware when not completed")] + public void should_call_execute_middleware_when_not_completed() + { + //arrange + var step1Body = A.Fake(); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); + WorkflowStep step1 = BuildFakeStep(step1Body); + Given1StepWorkflow(step1, "Workflow", 1); + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + ExecutionPointers = new ExecutionPointerCollection(new List + { + new ExecutionPointer { Id = "1", Active = true, StepId = 0 } + }) + }; + + //act + Subject.Execute(instance); + + //assert + A.CallTo(() => MiddlewareRunner.RunExecuteMiddleware(instance, A.Ignored)).MustHaveHappened(); + } + + [Fact(DisplayName = "Should not call post middleware when not completed")] + public void should_not_call_post_middleware_when_not_completed() + { + //arrange + var step1Body = A.Fake(); + A.CallTo(() => step1Body.RunAsync(A.Ignored)).Returns(ExecutionResult.Next()); + WorkflowStep step1 = BuildFakeStep(step1Body); + Given1StepWorkflow(step1, "Workflow", 1); + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = "Workflow", + Version = 1, + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Id = "001", + ExecutionPointers = new ExecutionPointerCollection(new List + { + new ExecutionPointer { Id = "1", Active = true, StepId = 0 } + }) + }; + + //act + Subject.Execute(instance); + + //assert + A.CallTo(() => MiddlewareRunner.RunPostMiddleware(instance, A.Ignored)).MustNotHaveHappened(); + } + [Fact(DisplayName = "Should trigger step hooks")] public void should_trigger_step_hooks() { From 2164e0cb273c793fe4a7b05603f401e0707ef310 Mon Sep 17 00:00:00 2001 From: glucaci Date: Fri, 16 Apr 2021 00:47:22 +0300 Subject: [PATCH 316/462] Cleanup --- .../Services/WorkflowMiddlewareRunner.cs | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs index 872721849..d341ed1ec 100644 --- a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs +++ b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs @@ -33,33 +33,46 @@ public async Task RunPreMiddleware(WorkflowInstance workflow, WorkflowDefinition } /// - public async Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinition def) + public Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinition def) { - var postMiddleware = _middleware - .Where(m => m.Phase == WorkflowMiddlewarePhase.PostWorkflow); - try - { - await RunWorkflowMiddleware(workflow, postMiddleware); - } - catch (Exception exception) - { - await HandleWorkflowMiddlewareError(def.OnPostMiddlewareError, exception); - } + return RunWorkflowMiddlewareWithErrorHandling( + workflow, + WorkflowMiddlewarePhase.PostWorkflow, + def.OnPostMiddlewareError); } /// - public async Task RunExecuteMiddleware(WorkflowInstance workflow, WorkflowDefinition def) + public Task RunExecuteMiddleware(WorkflowInstance workflow, WorkflowDefinition def) { - var executeMiddleware = _middleware - .Where(m => m.Phase == WorkflowMiddlewarePhase.ExecuteWorkflow); + return RunWorkflowMiddlewareWithErrorHandling( + workflow, + WorkflowMiddlewarePhase.ExecuteWorkflow, + def.OnExecuteMiddlewareError); + } + + public async Task RunWorkflowMiddlewareWithErrorHandling( + WorkflowInstance workflow, + WorkflowMiddlewarePhase phase, + Type middlewareErrorType) + { + var middleware = _middleware.Where(m => m.Phase == phase); try { - await RunWorkflowMiddleware(workflow, executeMiddleware); + await RunWorkflowMiddleware(workflow, middleware); } catch (Exception exception) { - await HandleWorkflowMiddlewareError(def.OnExecuteMiddlewareError, exception); + var errorHandlerType = middlewareErrorType ?? typeof(IWorkflowMiddlewareErrorHandler); + + using (var scope = _serviceProvider.CreateScope()) + { + var typeInstance = scope.ServiceProvider.GetService(errorHandlerType); + if (typeInstance is IWorkflowMiddlewareErrorHandler handler) + { + await handler.HandleAsync(exception); + } + } } } @@ -73,19 +86,5 @@ private static Task RunWorkflowMiddleware( (previous, middleware) => () => middleware.HandleAsync(workflow, previous))(); } - - private async Task HandleWorkflowMiddlewareError(Type middlewareErrorType, Exception exception) - { - var errorHandlerType = middlewareErrorType ?? typeof(IWorkflowMiddlewareErrorHandler); - - using (var scope = _serviceProvider.CreateScope()) - { - var typeInstance = scope.ServiceProvider.GetService(errorHandlerType); - if (typeInstance is IWorkflowMiddlewareErrorHandler handler) - { - await handler.HandleAsync(exception); - } - } - } } } From 8fd8bba630e645b309e3c932c749f9762b25326d Mon Sep 17 00:00:00 2001 From: glucaci Date: Fri, 16 Apr 2021 00:48:31 +0300 Subject: [PATCH 317/462] Cleanup --- src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs index d341ed1ec..d904c5aa5 100644 --- a/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs +++ b/src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs @@ -82,9 +82,9 @@ private static Task RunWorkflowMiddleware( { return middlewareCollection .Reverse() - .Aggregate(NoopWorkflowDelegate, - (previous, middleware) => - () => middleware.HandleAsync(workflow, previous))(); + .Aggregate( + NoopWorkflowDelegate, + (previous, middleware) => () => middleware.HandleAsync(workflow, previous))(); } } } From ead1dcba97dc9c081490449a15b352a387ebc580 Mon Sep 17 00:00:00 2001 From: Bruce Zlata Date: Fri, 16 Apr 2021 08:44:40 +0800 Subject: [PATCH 318/462] Change cosmosclient to private and add new line by end of file --- .../WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs | 2 +- .../WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs b/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs index bf5c2275c..fd9233164 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs @@ -6,4 +6,4 @@ public interface ICosmosDbClient { CosmosClient GetCosmosClient(); } -} \ No newline at end of file +} diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs index cda487e14..337e4de77 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs @@ -8,7 +8,7 @@ public class CosmosDbClient : ICosmosDbClient, IDisposable { private bool isDisposed = false; - public CosmosClient _client; + private CosmosClient _client; public CosmosDbClient(string connectionString) { @@ -45,4 +45,4 @@ protected virtual void Dispose(bool disposing) } } } -} \ No newline at end of file +} From 045cb49ecaf3a89ded5ee4184c16b70030573306 Mon Sep 17 00:00:00 2001 From: glucaci Date: Sun, 18 Apr 2021 23:19:59 +0300 Subject: [PATCH 319/462] Add release notes and increment minor --- ReleaseNotes/3.4.0.md | 55 ++++++++++++++++++++++++++++ src/WorkflowCore/WorkflowCore.csproj | 8 ++-- 2 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 ReleaseNotes/3.4.0.md diff --git a/ReleaseNotes/3.4.0.md b/ReleaseNotes/3.4.0.md new file mode 100644 index 000000000..ca724c4b2 --- /dev/null +++ b/ReleaseNotes/3.4.0.md @@ -0,0 +1,55 @@ +# Workflow Core 3.4.0 + +## Execute Workflow Middleware + +These middleware get run after each workflow execution and can be used to perform additional actions or build metrics/statistics for all workflows in your app. + +The following example illustrates how you can use a execute workflow middleware to build [prometheus](https://prometheus.io/) metrics. + +Note that you use `WorkflowMiddlewarePhase.ExecuteWorkflow` to specify that it runs after each workflow execution. + +**Important:** You should call `next` as part of the workflow middleware to ensure that the next workflow in the chain runs. + +```cs +public class MetricsMiddleware : IWorkflowMiddleware +{ + private readonly ConcurrentHashSet() _suspendedWorkflows = + new ConcurrentHashSet(); + + private readonly Counter _completed; + private readonly Counter _suspended; + + public MetricsMiddleware() + { + _completed = Prometheus.Metrics.CreateCounter( + "workflow_completed", "Workflow completed"); + + _suspended = Prometheus.Metrics.CreateCounter( + "workflow_suspended", "Workflow suspended"); + } + + public WorkflowMiddlewarePhase Phase => + WorkflowMiddlewarePhase.ExecuteWorkflow; + + public Task HandleAsync( + WorkflowInstance workflow, + WorkflowDelegate next) + { + switch (workflow.Status) + { + case WorkflowStatus.Complete: + if (_suspendedWorkflows.TryRemove(workflow.Id)) + { + _suspended.Dec(); + } + _completed.Inc(); + break; + case WorkflowStatus.Suspended: + _suspended.Inc(); + break; + } + + return next(); + } +} +``` \ No newline at end of file diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 2ef5a4a1c..a439d3bac 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -15,12 +15,12 @@ false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.3.6 - 3.3.6.0 - 3.3.6.0 + 3.4.0 + 3.4.0.0 + 3.4.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.3.6 + 3.4.0 From 3f1bf247d8e7e64e8ad0fb72ce8a4edc08abd621 Mon Sep 17 00:00:00 2001 From: Bruce Zlata Date: Mon, 19 Apr 2021 13:54:38 +0800 Subject: [PATCH 320/462] Naming to CosmosClientFactory --- .../{ICosmosDbClient.cs => ICosmosClientFactory.cs} | 2 +- .../ServiceCollectionExtensions.cs | 6 +++--- .../{CosmosDbClient.cs => CosmosClientFactory.cs} | 4 ++-- .../Services/CosmosDbPersistenceProvider.cs | 12 ++++++------ .../Services/CosmosDbProvisioner.cs | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) rename src/providers/WorkflowCore.Providers.Azure/Interface/{ICosmosDbClient.cs => ICosmosClientFactory.cs} (76%) rename src/providers/WorkflowCore.Providers.Azure/Services/{CosmosDbClient.cs => CosmosClientFactory.cs} (88%) diff --git a/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs b/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosClientFactory.cs similarity index 76% rename from src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs rename to src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosClientFactory.cs index fd9233164..cf8b44c47 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbClient.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosClientFactory.cs @@ -2,7 +2,7 @@ namespace WorkflowCore.Providers.Azure.Interface { - public interface ICosmosDbClient + public interface ICosmosClientFactory { CosmosClient GetCosmosClient(); } diff --git a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs index bdfd29d1e..96fff8f1d 100644 --- a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs @@ -28,9 +28,9 @@ public static WorkflowOptions UseAzureServiceBusEventHub( public static WorkflowOptions UseCosmosDbPersistence(this WorkflowOptions options, string connectionString, string databaseId) { - options.Services.AddSingleton(sp => new CosmosDbClient(connectionString)); - options.Services.AddTransient(sp => new CosmosDbProvisioner(sp.GetService(), sp.GetService())); - options.UsePersistence(sp => new CosmosDbPersistenceProvider(sp.GetService(), databaseId, sp.GetService())); + options.Services.AddSingleton(sp => new CosmosClientFactory(connectionString)); + options.Services.AddTransient(sp => new CosmosDbProvisioner(sp.GetService(), sp.GetService())); + options.UsePersistence(sp => new CosmosDbPersistenceProvider(sp.GetService(), databaseId, sp.GetService())); return options; } } diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosClientFactory.cs similarity index 88% rename from src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs rename to src/providers/WorkflowCore.Providers.Azure/Services/CosmosClientFactory.cs index 337e4de77..9cb4cc572 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbClient.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosClientFactory.cs @@ -4,13 +4,13 @@ namespace WorkflowCore.Providers.Azure.Services { - public class CosmosDbClient : ICosmosDbClient, IDisposable + public class CosmosClientFactory : ICosmosClientFactory, IDisposable { private bool isDisposed = false; private CosmosClient _client; - public CosmosDbClient(string connectionString) + public CosmosClientFactory(string connectionString) { _client = new CosmosClient(connectionString); } diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs index e3fbc97f0..a666322aa 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs @@ -19,19 +19,19 @@ public class CosmosDbPersistenceProvider : IPersistenceProvider private ICosmosDbProvisioner _provisioner; private string _dbId; - private ICosmosDbClient _client; + private ICosmosClientFactory _clientFactory; private Lazy _workflowContainer; private Lazy _eventContainer; private Lazy _subscriptionContainer; - public CosmosDbPersistenceProvider(ICosmosDbClient client, string dbId, ICosmosDbProvisioner provisioner) + public CosmosDbPersistenceProvider(ICosmosClientFactory clientFactory, string dbId, ICosmosDbProvisioner provisioner) { _provisioner = provisioner; _dbId = dbId; - _client = client; - _workflowContainer = new Lazy(() => _client.GetCosmosClient().GetDatabase(_dbId).GetContainer(WorkflowContainerName)); - _eventContainer = new Lazy(() => _client.GetCosmosClient().GetDatabase(_dbId).GetContainer(EventContainerName)); - _subscriptionContainer = new Lazy(() => _client.GetCosmosClient().GetDatabase(_dbId).GetContainer(SubscriptionContainerName)); + _clientFactory = clientFactory; + _workflowContainer = new Lazy(() => _clientFactory.GetCosmosClient().GetDatabase(_dbId).GetContainer(WorkflowContainerName)); + _eventContainer = new Lazy(() => _clientFactory.GetCosmosClient().GetDatabase(_dbId).GetContainer(EventContainerName)); + _subscriptionContainer = new Lazy(() => _clientFactory.GetCosmosClient().GetDatabase(_dbId).GetContainer(SubscriptionContainerName)); } public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs index 359ef799e..3d825dd01 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs @@ -9,9 +9,9 @@ namespace WorkflowCore.Providers.Azure.Services public class CosmosDbProvisioner : ICosmosDbProvisioner { - private ICosmosDbClient _client; + private ICosmosClientFactory _client; - public CosmosDbProvisioner(ICosmosDbClient client, ILoggerFactory loggerFactory) + public CosmosDbProvisioner(ICosmosClientFactory client, ILoggerFactory loggerFactory) { _client = client; } From 15df8315b0e0bb96fa1bedb1a3dcdde5f46dd6c4 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 19 Apr 2021 09:49:04 +0300 Subject: [PATCH 321/462] Fix suspended workflow counter --- ReleaseNotes/3.4.0.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/ReleaseNotes/3.4.0.md b/ReleaseNotes/3.4.0.md index ca724c4b2..e3708be5d 100644 --- a/ReleaseNotes/3.4.0.md +++ b/ReleaseNotes/3.4.0.md @@ -38,18 +38,27 @@ public class MetricsMiddleware : IWorkflowMiddleware switch (workflow.Status) { case WorkflowStatus.Complete: - if (_suspendedWorkflows.TryRemove(workflow.Id)) - { - _suspended.Dec(); - } + TryDecrementSuspended(workflow); _completed.Inc(); break; case WorkflowStatus.Suspended: + _suspendedWorkflows.Add(workflow.Id); _suspended.Inc(); break; + default: + TryDecrementSuspended(workflow); + break; } return next(); } + + private void TryDecrementSuspended(WorkflowInstance workflow) + { + if (_suspendedWorkflows.TryRemove(workflow.Id)) + { + _suspended.Dec(); + } + } } ``` \ No newline at end of file From 301b9fd7aac62eb280ae078475e32d3915da77c2 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 19 Apr 2021 10:54:05 +0300 Subject: [PATCH 322/462] Add draft API --- .../Interface/Persistence/ITransaction.cs | 10 ++++++++++ .../Interface/Persistence/IWorkflowRepository.cs | 2 +- src/WorkflowCore/Services/WorkflowController.cs | 4 ++-- .../Services/MongoPersistenceProvider.cs | 12 ++++++++++-- .../Services/MongoTransaction.cs | 15 +++++++++++++++ 5 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 src/WorkflowCore/Interface/Persistence/ITransaction.cs create mode 100644 src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoTransaction.cs diff --git a/src/WorkflowCore/Interface/Persistence/ITransaction.cs b/src/WorkflowCore/Interface/Persistence/ITransaction.cs new file mode 100644 index 000000000..cbb51e778 --- /dev/null +++ b/src/WorkflowCore/Interface/Persistence/ITransaction.cs @@ -0,0 +1,10 @@ +namespace WorkflowCore.Interface +{ + public interface ITransaction + { + /// + /// The transaction session specific for each storage provider. + /// + object Session { get; } + } +} \ No newline at end of file diff --git a/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs b/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs index 333423561..62545ba6f 100644 --- a/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs +++ b/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs @@ -7,7 +7,7 @@ namespace WorkflowCore.Interface { public interface IWorkflowRepository { - Task CreateNewWorkflow(WorkflowInstance workflow); + Task CreateNewWorkflow(WorkflowInstance workflow, ITransaction transaction = null); Task PersistWorkflow(WorkflowInstance workflow); diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 6edb63aa7..1fb97f821 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -53,7 +53,7 @@ public Task StartWorkflow(string workflowId, TData data = null, s return StartWorkflow(workflowId, null, data, reference); } - public async Task StartWorkflow(string workflowId, int? version, TData data = null, string reference=null) + public async Task StartWorkflow(string workflowId, int? version, TData data = null, string reference=null, ITransaction transaction = null) where TData : class, new() { @@ -91,7 +91,7 @@ public async Task StartWorkflow(string workflowId, int? version, await middlewareRunner.RunPreMiddleware(wf, def); } - string id = await _persistenceStore.CreateNewWorkflow(wf); + string id = await _persistenceStore.CreateNewWorkflow(wf, transaction); await _queueProvider.QueueWork(id, QueueType.Workflow); await _queueProvider.QueueWork(id, QueueType.Index); await _eventHub.PublishNotification(new WorkflowStarted diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 041b9ae96..5a9ebab62 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -122,9 +122,17 @@ static void CreateIndexes(MongoPersistenceProvider instance) private IMongoCollection ExecutionErrors => _database.GetCollection("wfc.execution_errors"); - public async Task CreateNewWorkflow(WorkflowInstance workflow) + public async Task CreateNewWorkflow(WorkflowInstance workflow, ITransaction transaction = null) { - await WorkflowInstances.InsertOneAsync(workflow); + if (transaction?.Session is IClientSessionHandle clientSessionHandle) + { + await WorkflowInstances.InsertOneAsync(clientSessionHandle, workflow); + } + else + { + await WorkflowInstances.InsertOneAsync(workflow); + } + return workflow.Id; } diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoTransaction.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoTransaction.cs new file mode 100644 index 000000000..678e92e28 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoTransaction.cs @@ -0,0 +1,15 @@ +using MongoDB.Driver; +using WorkflowCore.Interface; + +namespace WorkflowCore.Persistence.MongoDB.Services +{ + public class MongoTransaction : ITransaction + { + public MongoTransaction(IClientSessionHandle session) + { + Session = session; + } + + public object Session { get; } + } +} \ No newline at end of file From 3348dcacb6f6d842596bff374f4339f8d5e55f18 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 19 Apr 2021 10:55:56 +0300 Subject: [PATCH 323/462] Remove unnecessary file --- .../Services/MongoTransaction.cs | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoTransaction.cs diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoTransaction.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoTransaction.cs deleted file mode 100644 index 678e92e28..000000000 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoTransaction.cs +++ /dev/null @@ -1,15 +0,0 @@ -using MongoDB.Driver; -using WorkflowCore.Interface; - -namespace WorkflowCore.Persistence.MongoDB.Services -{ - public class MongoTransaction : ITransaction - { - public MongoTransaction(IClientSessionHandle session) - { - Session = session; - } - - public object Session { get; } - } -} \ No newline at end of file From b979c2156885e7e77b739ebee5d24c7a9bbc8a27 Mon Sep 17 00:00:00 2001 From: Bruce Zlata Date: Thu, 22 Apr 2021 16:47:26 +0800 Subject: [PATCH 324/462] naming to client factory --- .../Services/CosmosDbProvisioner.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs index 3d825dd01..607b4c1cd 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs @@ -9,16 +9,16 @@ namespace WorkflowCore.Providers.Azure.Services public class CosmosDbProvisioner : ICosmosDbProvisioner { - private ICosmosClientFactory _client; + private ICosmosClientFactory _clientFactory; - public CosmosDbProvisioner(ICosmosClientFactory client, ILoggerFactory loggerFactory) + public CosmosDbProvisioner(ICosmosClientFactory clientFactory, ILoggerFactory loggerFactory) { - _client = client; + _clientFactory = clientFactory; } public async Task Provision(string dbId) { - var dbResp = await _client.GetCosmosClient().CreateDatabaseIfNotExistsAsync(dbId); + var dbResp = await _clientFactory.GetCosmosClient().CreateDatabaseIfNotExistsAsync(dbId); var wfIndexPolicy = new IndexingPolicy(); wfIndexPolicy.IncludedPaths.Add(new IncludedPath { Path = @"/*" }); wfIndexPolicy.ExcludedPaths.Add(new ExcludedPath { Path = @"/ExecutionPointers/?" }); @@ -32,6 +32,5 @@ public async Task Provision(string dbId) dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(CosmosDbPersistenceProvider.SubscriptionContainerName, @"/id")) ); } - } } \ No newline at end of file From fb25722923229484604821965287fbe30dee86dc Mon Sep 17 00:00:00 2001 From: Bruce Zlata Date: Thu, 22 Apr 2021 16:48:57 +0800 Subject: [PATCH 325/462] new line at the end of file --- .../Services/CosmosDbProvisioner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs index 607b4c1cd..08e51c2f4 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs @@ -33,4 +33,4 @@ public async Task Provision(string dbId) ); } } -} \ No newline at end of file +} From e3a49d2dc2a923fb80e836e8b4e0601b0248189e Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Fri, 23 Apr 2021 11:11:58 +0300 Subject: [PATCH 326/462] Add cancellationToken support Change CosmosDB methods to be more async: https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.cosmos.container.getitemlinqqueryable?view=azure-dotnet#examples --- .../Interface/Persistence/IEventRepository.cs | 13 +- .../Persistence/IPersistenceProvider.cs | 3 +- .../Persistence/ISubscriptionRepository.cs | 15 +- .../Persistence/IWorkflowRepository.cs | 11 +- .../Services/BackgroundTasks/EventConsumer.cs | 20 +- .../BackgroundTasks/WorkflowConsumer.cs | 16 +- .../MemoryPersistenceProvider.cs | 39 ++-- .../TransientMemoryPersistenceProvider.cs | 40 ++-- .../Services/SyncWorkflowRunner.cs | 4 +- .../EntityFrameworkPersistenceProvider.cs | 93 +++++----- .../Services/MongoPersistenceProvider.cs | 85 ++++----- .../Services/RavendbPersistenceProvider.cs | 81 +++++---- .../Services/DynamoPersistenceProvider.cs | 75 ++++---- .../Services/CosmosDbPersistenceProvider.cs | 171 ++++++++++++------ .../Services/RedisPersistenceProvider.cs | 45 ++--- 15 files changed, 384 insertions(+), 327 deletions(-) diff --git a/src/WorkflowCore/Interface/Persistence/IEventRepository.cs b/src/WorkflowCore/Interface/Persistence/IEventRepository.cs index 63abdd43c..75651c24b 100644 --- a/src/WorkflowCore/Interface/Persistence/IEventRepository.cs +++ b/src/WorkflowCore/Interface/Persistence/IEventRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using WorkflowCore.Models; @@ -7,17 +8,17 @@ namespace WorkflowCore.Interface { public interface IEventRepository { - Task CreateEvent(Event newEvent); + Task CreateEvent(Event newEvent, CancellationToken cancellationToken = default); - Task GetEvent(string id); + Task GetEvent(string id, CancellationToken cancellationToken = default); - Task> GetRunnableEvents(DateTime asAt); + Task> GetRunnableEvents(DateTime asAt, CancellationToken cancellationToken = default); - Task> GetEvents(string eventName, string eventKey, DateTime asOf); + Task> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default); - Task MarkEventProcessed(string id); + Task MarkEventProcessed(string id, CancellationToken cancellationToken = default); - Task MarkEventUnprocessed(string id); + Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default); } } diff --git a/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs b/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs index 165dc7cbb..7bff56345 100644 --- a/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs +++ b/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using WorkflowCore.Models; @@ -8,7 +9,7 @@ namespace WorkflowCore.Interface public interface IPersistenceProvider : IWorkflowRepository, ISubscriptionRepository, IEventRepository { - Task PersistErrors(IEnumerable errors); + Task PersistErrors(IEnumerable errors, CancellationToken cancellationToken = default); void EnsureStoreExists(); diff --git a/src/WorkflowCore/Interface/Persistence/ISubscriptionRepository.cs b/src/WorkflowCore/Interface/Persistence/ISubscriptionRepository.cs index c37cf2f32..2f22a45f4 100644 --- a/src/WorkflowCore/Interface/Persistence/ISubscriptionRepository.cs +++ b/src/WorkflowCore/Interface/Persistence/ISubscriptionRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using WorkflowCore.Models; @@ -7,19 +8,19 @@ namespace WorkflowCore.Interface { public interface ISubscriptionRepository { - Task CreateEventSubscription(EventSubscription subscription); + Task CreateEventSubscription(EventSubscription subscription, CancellationToken cancellationToken = default); - Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf); + Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default); - Task TerminateSubscription(string eventSubscriptionId); + Task TerminateSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default); - Task GetSubscription(string eventSubscriptionId); + Task GetSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default); - Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf); + Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default); - Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry); + Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken cancellationToken = default); - Task ClearSubscriptionToken(string eventSubscriptionId, string token); + Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken cancellationToken = default); } } diff --git a/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs b/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs index 333423561..1da4275cd 100644 --- a/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs +++ b/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using WorkflowCore.Models; @@ -7,18 +8,18 @@ namespace WorkflowCore.Interface { public interface IWorkflowRepository { - Task CreateNewWorkflow(WorkflowInstance workflow); + Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default); - Task PersistWorkflow(WorkflowInstance workflow); + Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default); - Task> GetRunnableInstances(DateTime asAt); + Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default); [Obsolete] Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take); - Task GetWorkflowInstance(string Id); + Task GetWorkflowInstance(string Id, CancellationToken cancellationToken = default); - Task> GetWorkflowInstances(IEnumerable ids); + Task> GetWorkflowInstances(IEnumerable ids, CancellationToken cancellationToken = default); } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index bdd730fca..888ff8ed0 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -42,7 +42,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance try { cancellationToken.ThrowIfCancellationRequested(); - var evt = await _eventRepository.GetEvent(itemId); + var evt = await _eventRepository.GetEvent(itemId, cancellationToken); if (evt.IsProcessed) { _greylist.Add($"evt:{evt.Id}"); @@ -53,18 +53,18 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance IEnumerable subs = null; if (evt.EventData is ActivityResult) { - var activity = await _subscriptionRepository.GetSubscription((evt.EventData as ActivityResult).SubscriptionId); + var activity = await _subscriptionRepository.GetSubscription((evt.EventData as ActivityResult).SubscriptionId, cancellationToken); if (activity == null) { Logger.LogWarning($"Activity already processed - {(evt.EventData as ActivityResult).SubscriptionId}"); - await _eventRepository.MarkEventProcessed(itemId); + await _eventRepository.MarkEventProcessed(itemId, cancellationToken); return; } subs = new List() { activity }; } else { - subs = await _subscriptionRepository.GetSubscriptions(evt.EventName, evt.EventKey, evt.EventTime); + subs = await _subscriptionRepository.GetSubscriptions(evt.EventName, evt.EventKey, evt.EventTime, cancellationToken); } var toQueue = new HashSet(); @@ -74,7 +74,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance complete = complete && await SeedSubscription(evt, sub, toQueue, cancellationToken); if (complete) - await _eventRepository.MarkEventProcessed(itemId); + await _eventRepository.MarkEventProcessed(itemId, cancellationToken); foreach (var eventId in toQueue) await QueueProvider.QueueWork(eventId, QueueType.Event); @@ -88,12 +88,12 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance private async Task SeedSubscription(Event evt, EventSubscription sub, HashSet toQueue, CancellationToken cancellationToken) { - foreach (var eventId in await _eventRepository.GetEvents(sub.EventName, sub.EventKey, sub.SubscribeAsOf)) + foreach (var eventId in await _eventRepository.GetEvents(sub.EventName, sub.EventKey, sub.SubscribeAsOf, cancellationToken)) { if (eventId == evt.Id) continue; - var siblingEvent = await _eventRepository.GetEvent(eventId); + var siblingEvent = await _eventRepository.GetEvent(eventId, cancellationToken); if ((!siblingEvent.IsProcessed) && (siblingEvent.EventTime < evt.EventTime)) { await QueueProvider.QueueWork(eventId, QueueType.Event); @@ -112,7 +112,7 @@ private async Task SeedSubscription(Event evt, EventSubscription sub, Hash try { - var workflow = await _workflowRepository.GetWorkflowInstance(sub.WorkflowId); + var workflow = await _workflowRepository.GetWorkflowInstance(sub.WorkflowId, cancellationToken); IEnumerable pointers = null; if (!string.IsNullOrEmpty(sub.ExecutionPointerId)) @@ -127,8 +127,8 @@ private async Task SeedSubscription(Event evt, EventSubscription sub, Hash p.Active = true; } workflow.NextExecution = 0; - await _workflowRepository.PersistWorkflow(workflow); - await _subscriptionRepository.TerminateSubscription(sub.Id); + await _workflowRepository.PersistWorkflow(workflow, cancellationToken); + await _subscriptionRepository.TerminateSubscription(sub.Id, cancellationToken); return true; } catch (Exception ex) diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 6ce3776b5..36327a28f 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -43,7 +43,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance try { cancellationToken.ThrowIfCancellationRequested(); - workflow = await _persistenceStore.GetWorkflowInstance(itemId); + workflow = await _persistenceStore.GetWorkflowInstance(itemId, cancellationToken); if (workflow.Status == WorkflowStatus.Runnable) { try @@ -52,7 +52,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance } finally { - await _persistenceStore.PersistWorkflow(workflow); + await _persistenceStore.PersistWorkflow(workflow, cancellationToken); await QueueProvider.QueueWork(itemId, QueueType.Index); _greylist.Remove($"wf:{itemId}"); } @@ -65,10 +65,10 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { foreach (var sub in result.Subscriptions) { - await SubscribeEvent(sub, _persistenceStore); + await SubscribeEvent(sub, _persistenceStore, cancellationToken); } - await _persistenceStore.PersistErrors(result.Errors); + await _persistenceStore.PersistErrors(result.Errors, cancellationToken); var readAheadTicks = _datetimeProvider.UtcNow.Add(Options.PollInterval).Ticks; @@ -81,18 +81,18 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance } - private async Task SubscribeEvent(EventSubscription subscription, IPersistenceProvider persistenceStore) + private async Task SubscribeEvent(EventSubscription subscription, IPersistenceProvider persistenceStore, CancellationToken cancellationToken) { //TODO: move to own class Logger.LogDebug("Subscribing to event {0} {1} for workflow {2} step {3}", subscription.EventName, subscription.EventKey, subscription.WorkflowId, subscription.StepId); - await persistenceStore.CreateEventSubscription(subscription); + await persistenceStore.CreateEventSubscription(subscription, cancellationToken); if (subscription.EventName != Event.EventTypeActivity) { - var events = await persistenceStore.GetEvents(subscription.EventName, subscription.EventKey, subscription.SubscribeAsOf); + var events = await persistenceStore.GetEvents(subscription.EventName, subscription.EventKey, subscription.SubscribeAsOf, cancellationToken); foreach (var evt in events) { - await persistenceStore.MarkEventUnprocessed(evt); + await persistenceStore.MarkEventUnprocessed(evt, cancellationToken); await QueueProvider.QueueWork(evt, QueueType.Event); } } diff --git a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs index fd27db8ca..468967972 100644 --- a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -24,7 +25,7 @@ public class MemoryPersistenceProvider : ISingletonMemoryProvider private readonly List _events = new List(); private readonly List _errors = new List(); - public async Task CreateNewWorkflow(WorkflowInstance workflow) + public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken _ = default) { lock (_instances) { @@ -34,7 +35,7 @@ public async Task CreateNewWorkflow(WorkflowInstance workflow) } } - public async Task PersistWorkflow(WorkflowInstance workflow) + public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken _ = default) { lock (_instances) { @@ -44,7 +45,7 @@ public async Task PersistWorkflow(WorkflowInstance workflow) } } - public async Task> GetRunnableInstances(DateTime asAt) + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken _ = default) { lock (_instances) { @@ -53,7 +54,7 @@ public async Task> GetRunnableInstances(DateTime asAt) } } - public async Task GetWorkflowInstance(string Id) + public async Task GetWorkflowInstance(string Id, CancellationToken _ = default) { lock (_instances) { @@ -61,7 +62,7 @@ public async Task GetWorkflowInstance(string Id) } } - public async Task> GetWorkflowInstances(IEnumerable ids) + public async Task> GetWorkflowInstances(IEnumerable ids, CancellationToken _ = default) { if (ids == null) { @@ -105,7 +106,7 @@ public async Task> GetWorkflowInstances(WorkflowSt } - public async Task CreateEventSubscription(EventSubscription subscription) + public async Task CreateEventSubscription(EventSubscription subscription, CancellationToken _ = default) { lock (_subscriptions) { @@ -115,7 +116,7 @@ public async Task CreateEventSubscription(EventSubscription subscription } } - public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken _ = default) { lock (_subscriptions) { @@ -124,7 +125,7 @@ public async Task> GetSubscriptions(string eventN } } - public async Task TerminateSubscription(string eventSubscriptionId) + public async Task TerminateSubscription(string eventSubscriptionId, CancellationToken _ = default) { lock (_subscriptions) { @@ -133,7 +134,7 @@ public async Task TerminateSubscription(string eventSubscriptionId) } } - public Task GetSubscription(string eventSubscriptionId) + public Task GetSubscription(string eventSubscriptionId, CancellationToken _ = default) { lock (_subscriptions) { @@ -142,7 +143,7 @@ public Task GetSubscription(string eventSubscriptionId) } } - public Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + public Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken _ = default) { lock (_subscriptions) { @@ -152,7 +153,7 @@ public Task GetFirstOpenSubscription(string eventName, string } } - public Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + public Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken _ = default) { lock (_subscriptions) { @@ -165,7 +166,7 @@ public Task SetSubscriptionToken(string eventSubscriptionId, string token, } } - public Task ClearSubscriptionToken(string eventSubscriptionId, string token) + public Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken _ = default) { lock (_subscriptions) { @@ -184,7 +185,7 @@ public void EnsureStoreExists() { } - public async Task CreateEvent(Event newEvent) + public async Task CreateEvent(Event newEvent, CancellationToken _ = default) { lock (_events) { @@ -194,7 +195,7 @@ public async Task CreateEvent(Event newEvent) } } - public async Task MarkEventProcessed(string id) + public async Task MarkEventProcessed(string id, CancellationToken _ = default) { lock (_events) { @@ -204,7 +205,7 @@ public async Task MarkEventProcessed(string id) } } - public async Task> GetRunnableEvents(DateTime asAt) + public async Task> GetRunnableEvents(DateTime asAt, CancellationToken _ = default) { lock (_events) { @@ -216,7 +217,7 @@ public async Task> GetRunnableEvents(DateTime asAt) } } - public async Task GetEvent(string id) + public async Task GetEvent(string id, CancellationToken _ = default) { lock (_events) { @@ -224,7 +225,7 @@ public async Task GetEvent(string id) } } - public async Task> GetEvents(string eventName, string eventKey, DateTime asOf) + public async Task> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken _ = default) { lock (_events) { @@ -236,7 +237,7 @@ public async Task> GetEvents(string eventName, string eventK } } - public async Task MarkEventUnprocessed(string id) + public async Task MarkEventUnprocessed(string id, CancellationToken _ = default) { lock (_events) { @@ -248,7 +249,7 @@ public async Task MarkEventUnprocessed(string id) } } - public async Task PersistErrors(IEnumerable errors) + public async Task PersistErrors(IEnumerable errors, CancellationToken _ = default) { lock (errors) { diff --git a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs index 6b3084a89..62a0a8aba 100644 --- a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Threading; using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -16,45 +16,45 @@ public TransientMemoryPersistenceProvider(ISingletonMemoryProvider innerService) _innerService = innerService; } - public Task CreateEvent(Event newEvent) => _innerService.CreateEvent(newEvent); + public Task CreateEvent(Event newEvent, CancellationToken _ = default) => _innerService.CreateEvent(newEvent); - public Task CreateEventSubscription(EventSubscription subscription) => _innerService.CreateEventSubscription(subscription); + public Task CreateEventSubscription(EventSubscription subscription, CancellationToken _ = default) => _innerService.CreateEventSubscription(subscription); - public Task CreateNewWorkflow(WorkflowInstance workflow) => _innerService.CreateNewWorkflow(workflow); + public Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken _ = default) => _innerService.CreateNewWorkflow(workflow); public void EnsureStoreExists() => _innerService.EnsureStoreExists(); - public Task GetEvent(string id) => _innerService.GetEvent(id); + public Task GetEvent(string id, CancellationToken _ = default) => _innerService.GetEvent(id); - public Task> GetEvents(string eventName, string eventKey, DateTime asOf) => _innerService.GetEvents(eventName, eventKey, asOf); + public Task> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken _ = default) => _innerService.GetEvents(eventName, eventKey, asOf); - public Task> GetRunnableEvents(DateTime asAt) => _innerService.GetRunnableEvents(asAt); + public Task> GetRunnableEvents(DateTime asAt, CancellationToken _ = default) => _innerService.GetRunnableEvents(asAt); - public Task> GetRunnableInstances(DateTime asAt) => _innerService.GetRunnableInstances(asAt); + public Task> GetRunnableInstances(DateTime asAt, CancellationToken _ = default) => _innerService.GetRunnableInstances(asAt); - public Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) => _innerService.GetSubscriptions(eventName, eventKey, asOf); + public Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken _ = default) => _innerService.GetSubscriptions(eventName, eventKey, asOf); - public Task GetWorkflowInstance(string Id) => _innerService.GetWorkflowInstance(Id); + public Task GetWorkflowInstance(string Id, CancellationToken _ = default) => _innerService.GetWorkflowInstance(Id); - public Task> GetWorkflowInstances(IEnumerable ids) => _innerService.GetWorkflowInstances(ids); + public Task> GetWorkflowInstances(IEnumerable ids, CancellationToken _ = default) => _innerService.GetWorkflowInstances(ids); public Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) => _innerService.GetWorkflowInstances(status, type, createdFrom, createdTo, skip, take); - public Task MarkEventProcessed(string id) => _innerService.MarkEventProcessed(id); + public Task MarkEventProcessed(string id, CancellationToken _ = default) => _innerService.MarkEventProcessed(id); - public Task MarkEventUnprocessed(string id) => _innerService.MarkEventUnprocessed(id); + public Task MarkEventUnprocessed(string id, CancellationToken _ = default) => _innerService.MarkEventUnprocessed(id); - public Task PersistErrors(IEnumerable errors) => _innerService.PersistErrors(errors); + public Task PersistErrors(IEnumerable errors, CancellationToken _ = default) => _innerService.PersistErrors(errors); - public Task PersistWorkflow(WorkflowInstance workflow) => _innerService.PersistWorkflow(workflow); + public Task PersistWorkflow(WorkflowInstance workflow, CancellationToken _ = default) => _innerService.PersistWorkflow(workflow); - public Task TerminateSubscription(string eventSubscriptionId) => _innerService.TerminateSubscription(eventSubscriptionId); - public Task GetSubscription(string eventSubscriptionId) => _innerService.GetSubscription(eventSubscriptionId); + public Task TerminateSubscription(string eventSubscriptionId, CancellationToken _ = default) => _innerService.TerminateSubscription(eventSubscriptionId); + public Task GetSubscription(string eventSubscriptionId, CancellationToken _ = default) => _innerService.GetSubscription(eventSubscriptionId); - public Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) => _innerService.GetFirstOpenSubscription(eventName, eventKey, asOf); + public Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken _ = default) => _innerService.GetFirstOpenSubscription(eventName, eventKey, asOf); - public Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) => _innerService.SetSubscriptionToken(eventSubscriptionId, token, workerId, expiry); + public Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken _ = default) => _innerService.SetSubscriptionToken(eventSubscriptionId, token, workerId, expiry); - public Task ClearSubscriptionToken(string eventSubscriptionId, string token) => _innerService.ClearSubscriptionToken(eventSubscriptionId, token); + public Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken _ = default) => _innerService.ClearSubscriptionToken(eventSubscriptionId, token); } } diff --git a/src/WorkflowCore/Services/SyncWorkflowRunner.cs b/src/WorkflowCore/Services/SyncWorkflowRunner.cs index 293c976a0..5a9b7d5b5 100644 --- a/src/WorkflowCore/Services/SyncWorkflowRunner.cs +++ b/src/WorkflowCore/Services/SyncWorkflowRunner.cs @@ -74,7 +74,7 @@ public async Task RunWorkflowSync(string workflowId, in var id = Guid.NewGuid().ToString(); if (persistSate) - id = await _persistenceStore.CreateNewWorkflow(wf); + id = await _persistenceStore.CreateNewWorkflow(wf, token); else wf.Id = id; @@ -91,7 +91,7 @@ public async Task RunWorkflowSync(string workflowId, in { await _executor.Execute(wf); if (persistSate) - await _persistenceStore.PersistWorkflow(wf); + await _persistenceStore.PersistWorkflow(wf, token); } } finally diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs index 3af7354de..e0c7082fd 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs @@ -2,15 +2,12 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.DependencyInjection; using WorkflowCore.Interface; using WorkflowCore.Persistence.EntityFramework.Models; using WorkflowCore.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; using WorkflowCore.Persistence.EntityFramework.Interfaces; +using System.Threading; namespace WorkflowCore.Persistence.EntityFramework.Services { @@ -27,31 +24,31 @@ public EntityFrameworkPersistenceProvider(IWorkflowDbContextFactory contextFacto _canMigrateDB = canMigrateDB; } - public async Task CreateEventSubscription(EventSubscription subscription) + public async Task CreateEventSubscription(EventSubscription subscription, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { subscription.Id = Guid.NewGuid().ToString(); var persistable = subscription.ToPersistable(); var result = db.Set().Add(persistable); - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); return subscription.Id; } } - public async Task CreateNewWorkflow(WorkflowInstance workflow) + public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { workflow.Id = Guid.NewGuid().ToString(); var persistable = workflow.ToPersistable(); var result = db.Set().Add(persistable); - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); return workflow.Id; } } - public async Task> GetRunnableInstances(DateTime asAt) + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { @@ -59,7 +56,7 @@ public async Task> GetRunnableInstances(DateTime asAt) var raw = await db.Set() .Where(x => x.NextExecution.HasValue && (x.NextExecution <= now) && (x.Status == WorkflowStatus.Runnable)) .Select(x => x.InstanceId) - .ToListAsync(); + .ToListAsync(cancellationToken); return raw.Select(s => s.ToString()).ToList(); } @@ -97,7 +94,7 @@ public async Task> GetWorkflowInstances(WorkflowSt } } - public async Task GetWorkflowInstance(string Id) + public async Task GetWorkflowInstance(string Id, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { @@ -106,7 +103,7 @@ public async Task GetWorkflowInstance(string Id) .Include(wf => wf.ExecutionPointers) .ThenInclude(ep => ep.ExtensionAttributes) .Include(wf => wf.ExecutionPointers) - .FirstAsync(x => x.InstanceId == uid); + .FirstAsync(x => x.InstanceId == uid, cancellationToken); if (raw == null) return null; @@ -115,7 +112,7 @@ public async Task GetWorkflowInstance(string Id) } } - public async Task> GetWorkflowInstances(IEnumerable ids) + public async Task> GetWorkflowInstances(IEnumerable ids, CancellationToken cancellationToken = default) { if (ids == null) { @@ -131,11 +128,11 @@ public async Task> GetWorkflowInstances(IEnumerabl .Include(wf => wf.ExecutionPointers) .Where(x => uids.Contains(x.InstanceId)); - return (await raw.ToListAsync()).Select(i => i.ToWorkflowInstance()); + return (await raw.ToListAsync(cancellationToken)).Select(i => i.ToWorkflowInstance()); } } - public async Task PersistWorkflow(WorkflowInstance workflow) + public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { @@ -146,21 +143,21 @@ public async Task PersistWorkflow(WorkflowInstance workflow) .ThenInclude(ep => ep.ExtensionAttributes) .Include(wf => wf.ExecutionPointers) .AsTracking() - .FirstAsync(); + .FirstAsync(cancellationToken); var persistable = workflow.ToPersistable(existingEntity); - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); } } - public async Task TerminateSubscription(string eventSubscriptionId) + public async Task TerminateSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { var uid = new Guid(eventSubscriptionId); - var existing = await db.Set().FirstAsync(x => x.SubscriptionId == uid); + var existing = await db.Set().FirstAsync(x => x.SubscriptionId == uid, cancellationToken); db.Set().Remove(existing); - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); } } @@ -182,38 +179,38 @@ public virtual void EnsureStoreExists() } } - public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { asOf = asOf.ToUniversalTime(); var raw = await db.Set() .Where(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf) - .ToListAsync(); + .ToListAsync(cancellationToken); return raw.Select(item => item.ToEventSubscription()).ToList(); } } - public async Task CreateEvent(Event newEvent) + public async Task CreateEvent(Event newEvent, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { newEvent.Id = Guid.NewGuid().ToString(); var persistable = newEvent.ToPersistable(); var result = db.Set().Add(persistable); - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); return newEvent.Id; } } - public async Task GetEvent(string id) + public async Task GetEvent(string id, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { Guid uid = new Guid(id); var raw = await db.Set() - .FirstAsync(x => x.EventId == uid); + .FirstAsync(x => x.EventId == uid, cancellationToken); if (raw == null) return null; @@ -222,7 +219,7 @@ public async Task GetEvent(string id) } } - public async Task> GetRunnableEvents(DateTime asAt) + public async Task> GetRunnableEvents(DateTime asAt, CancellationToken cancellationToken = default) { var now = asAt.ToUniversalTime(); using (var db = ConstructDbContext()) @@ -232,13 +229,13 @@ public async Task> GetRunnableEvents(DateTime asAt) .Where(x => !x.IsProcessed) .Where(x => x.EventTime <= now) .Select(x => x.EventId) - .ToListAsync(); + .ToListAsync(cancellationToken); return raw.Select(s => s.ToString()).ToList(); } } - public async Task MarkEventProcessed(string id) + public async Task MarkEventProcessed(string id, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { @@ -246,14 +243,14 @@ public async Task MarkEventProcessed(string id) var existingEntity = await db.Set() .Where(x => x.EventId == uid) .AsTracking() - .FirstAsync(); + .FirstAsync(cancellationToken); existingEntity.IsProcessed = true; - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); } } - public async Task> GetEvents(string eventName, string eventKey, DateTime asOf) + public async Task> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken) { using (var db = ConstructDbContext()) { @@ -261,7 +258,7 @@ public async Task> GetEvents(string eventName, string eventK .Where(x => x.EventName == eventName && x.EventKey == eventKey) .Where(x => x.EventTime >= asOf) .Select(x => x.EventId) - .ToListAsync(); + .ToListAsync(cancellationToken); var result = new List(); @@ -272,7 +269,7 @@ public async Task> GetEvents(string eventName, string eventK } } - public async Task MarkEventUnprocessed(string id) + public async Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { @@ -280,14 +277,14 @@ public async Task MarkEventUnprocessed(string id) var existingEntity = await db.Set() .Where(x => x.EventId == uid) .AsTracking() - .FirstAsync(); + .FirstAsync(cancellationToken); existingEntity.IsProcessed = false; - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); } } - public async Task PersistErrors(IEnumerable errors) + public async Task PersistErrors(IEnumerable errors, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { @@ -298,7 +295,7 @@ public async Task PersistErrors(IEnumerable errors) { db.Set().Add(error.ToPersistable()); } - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); } } @@ -309,28 +306,28 @@ private WorkflowDbContext ConstructDbContext() return _contextFactory.Build(); } - public async Task GetSubscription(string eventSubscriptionId) + public async Task GetSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { var uid = new Guid(eventSubscriptionId); - var raw = await db.Set().FirstOrDefaultAsync(x => x.SubscriptionId == uid); + var raw = await db.Set().FirstOrDefaultAsync(x => x.SubscriptionId == uid, cancellationToken); return raw?.ToEventSubscription(); } } - public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { - var raw = await db.Set().FirstOrDefaultAsync(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf && x.ExternalToken == null); + var raw = await db.Set().FirstOrDefaultAsync(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf && x.ExternalToken == null, cancellationToken); return raw?.ToEventSubscription(); } } - public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { @@ -338,18 +335,18 @@ public async Task SetSubscriptionToken(string eventSubscriptionId, string var existingEntity = await db.Set() .Where(x => x.SubscriptionId == uid) .AsTracking() - .FirstAsync(); + .FirstAsync(cancellationToken); existingEntity.ExternalToken = token; existingEntity.ExternalWorkerId = workerId; existingEntity.ExternalTokenExpiry = expiry; - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); return true; } } - public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken cancellationToken = default) { using (var db = ConstructDbContext()) { @@ -357,7 +354,7 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke var existingEntity = await db.Set() .Where(x => x.SubscriptionId == uid) .AsTracking() - .FirstAsync(); + .FirstAsync(cancellationToken); if (existingEntity.ExternalToken != token) throw new InvalidOperationException(); @@ -365,7 +362,7 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke existingEntity.ExternalToken = null; existingEntity.ExternalWorkerId = null; existingEntity.ExternalTokenExpiry = null; - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); } } } diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 041b9ae96..79e9fe5b7 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -11,6 +11,7 @@ using MongoDB.Driver.Linq; using WorkflowCore.Interface; using WorkflowCore.Models; +using System.Threading; namespace WorkflowCore.Persistence.MongoDB.Services { @@ -122,42 +123,42 @@ static void CreateIndexes(MongoPersistenceProvider instance) private IMongoCollection ExecutionErrors => _database.GetCollection("wfc.execution_errors"); - public async Task CreateNewWorkflow(WorkflowInstance workflow) + public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { - await WorkflowInstances.InsertOneAsync(workflow); + await WorkflowInstances.InsertOneAsync(workflow, cancellationToken: cancellationToken); return workflow.Id; } - public async Task PersistWorkflow(WorkflowInstance workflow) + public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { - await WorkflowInstances.ReplaceOneAsync(x => x.Id == workflow.Id, workflow); + await WorkflowInstances.ReplaceOneAsync(x => x.Id == workflow.Id, workflow, cancellationToken: cancellationToken); } - public async Task> GetRunnableInstances(DateTime asAt) + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default) { var now = asAt.ToUniversalTime().Ticks; var query = WorkflowInstances .Find(x => x.NextExecution.HasValue && (x.NextExecution <= now) && (x.Status == WorkflowStatus.Runnable)) .Project(x => x.Id); - return await query.ToListAsync(); + return await query.ToListAsync(cancellationToken); } - public async Task GetWorkflowInstance(string Id) + public async Task GetWorkflowInstance(string Id, CancellationToken cancellationToken = default) { - var result = await WorkflowInstances.FindAsync(x => x.Id == Id); - return await result.FirstAsync(); + var result = await WorkflowInstances.FindAsync(x => x.Id == Id, cancellationToken: cancellationToken); + return await result.FirstAsync(cancellationToken); } - public async Task> GetWorkflowInstances(IEnumerable ids) + public async Task> GetWorkflowInstances(IEnumerable ids, CancellationToken cancellationToken = default) { if (ids == null) { return new List(); } - var result = await WorkflowInstances.FindAsync(x => ids.Contains(x.Id)); - return await result.ToListAsync(); + var result = await WorkflowInstances.FindAsync(x => ids.Contains(x.Id), cancellationToken: cancellationToken); + return await result.ToListAsync(cancellationToken); } public async Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take) @@ -179,50 +180,50 @@ public async Task> GetWorkflowInstances(WorkflowSt return await result.Skip(skip).Take(take).ToListAsync(); } - public async Task CreateEventSubscription(EventSubscription subscription) + public async Task CreateEventSubscription(EventSubscription subscription, CancellationToken cancellationToken = default) { - await EventSubscriptions.InsertOneAsync(subscription); + await EventSubscriptions.InsertOneAsync(subscription, cancellationToken: cancellationToken); return subscription.Id; } - public async Task TerminateSubscription(string eventSubscriptionId) + public async Task TerminateSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default) { - await EventSubscriptions.DeleteOneAsync(x => x.Id == eventSubscriptionId); + await EventSubscriptions.DeleteOneAsync(x => x.Id == eventSubscriptionId, cancellationToken); } - public async Task GetSubscription(string eventSubscriptionId) + public async Task GetSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default) { - var result = await EventSubscriptions.FindAsync(x => x.Id == eventSubscriptionId); - return await result.FirstOrDefaultAsync(); + var result = await EventSubscriptions.FindAsync(x => x.Id == eventSubscriptionId, cancellationToken: cancellationToken); + return await result.FirstOrDefaultAsync(cancellationToken); } - public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default) { var query = EventSubscriptions .Find(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf && x.ExternalToken == null); - return await query.FirstOrDefaultAsync(); + return await query.FirstOrDefaultAsync(cancellationToken); } - public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken cancellationToken = default) { var update = Builders.Update .Set(x => x.ExternalToken, token) .Set(x => x.ExternalTokenExpiry, expiry) .Set(x => x.ExternalWorkerId, workerId); - var result = await EventSubscriptions.UpdateOneAsync(x => x.Id == eventSubscriptionId && x.ExternalToken == null, update); + var result = await EventSubscriptions.UpdateOneAsync(x => x.Id == eventSubscriptionId && x.ExternalToken == null, update, cancellationToken: cancellationToken); return (result.ModifiedCount > 0); } - public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken cancellationToken = default) { var update = Builders.Update .Set(x => x.ExternalToken, null) .Set(x => x.ExternalTokenExpiry, null) .Set(x => x.ExternalWorkerId, null); - await EventSubscriptions.UpdateOneAsync(x => x.Id == eventSubscriptionId && x.ExternalToken == token, update); + await EventSubscriptions.UpdateOneAsync(x => x.Id == eventSubscriptionId && x.ExternalToken == token, update, cancellationToken: cancellationToken); } public void EnsureStoreExists() @@ -230,65 +231,65 @@ public void EnsureStoreExists() } - public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default) { var query = EventSubscriptions .Find(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf); - return await query.ToListAsync(); + return await query.ToListAsync(cancellationToken); } - public async Task CreateEvent(Event newEvent) + public async Task CreateEvent(Event newEvent, CancellationToken cancellationToken = default) { - await Events.InsertOneAsync(newEvent); + await Events.InsertOneAsync(newEvent, cancellationToken: cancellationToken); return newEvent.Id; } - public async Task GetEvent(string id) + public async Task GetEvent(string id, CancellationToken cancellationToken = default) { - var result = await Events.FindAsync(x => x.Id == id); - return await result.FirstAsync(); + var result = await Events.FindAsync(x => x.Id == id, cancellationToken: cancellationToken); + return await result.FirstAsync(cancellationToken); } - public async Task> GetRunnableEvents(DateTime asAt) + public async Task> GetRunnableEvents(DateTime asAt, CancellationToken cancellationToken = default) { var now = asAt.ToUniversalTime(); var query = Events .Find(x => !x.IsProcessed && x.EventTime <= now) .Project(x => x.Id); - return await query.ToListAsync(); + return await query.ToListAsync(cancellationToken); } - public async Task MarkEventProcessed(string id) + public async Task MarkEventProcessed(string id, CancellationToken cancellationToken = default) { var update = Builders.Update .Set(x => x.IsProcessed, true); - await Events.UpdateOneAsync(x => x.Id == id, update); + await Events.UpdateOneAsync(x => x.Id == id, update, cancellationToken: cancellationToken); } - public async Task> GetEvents(string eventName, string eventKey, DateTime asOf) + public async Task> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken) { var query = Events .Find(x => x.EventName == eventName && x.EventKey == eventKey && x.EventTime >= asOf) .Project(x => x.Id); - return await query.ToListAsync(); + return await query.ToListAsync(cancellationToken); } - public async Task MarkEventUnprocessed(string id) + public async Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default) { var update = Builders.Update .Set(x => x.IsProcessed, false); - await Events.UpdateOneAsync(x => x.Id == id, update); + await Events.UpdateOneAsync(x => x.Id == id, update, cancellationToken: cancellationToken); } - public async Task PersistErrors(IEnumerable errors) + public async Task PersistErrors(IEnumerable errors, CancellationToken cancellationToken = default) { if (errors.Any()) - await ExecutionErrors.InsertManyAsync(errors); + await ExecutionErrors.InsertManyAsync(errors, cancellationToken: cancellationToken); } } } diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs index 66d38b5aa..1bcaadff8 100644 --- a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -39,18 +40,18 @@ static void CreateIndexes(RavendbPersistenceProvider instance) } } - public async Task CreateNewWorkflow(WorkflowInstance workflow) + public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { - await session.StoreAsync(workflow); + await session.StoreAsync(workflow, cancellationToken); var id = workflow.Id; - await session.SaveChangesAsync(); + await session.SaveChangesAsync(cancellationToken); return id; } } - public async Task PersistWorkflow(WorkflowInstance workflow) + public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { @@ -65,11 +66,11 @@ public async Task PersistWorkflow(WorkflowInstance workflow) session.Advanced.Patch(workflow.Id, x => x.CreateTime, workflow.CreateTime); session.Advanced.Patch(workflow.Id, x => x.CompleteTime, workflow.CompleteTime); - await session.SaveChangesAsync(); + await session.SaveChangesAsync(cancellationToken); } } - public async Task> GetRunnableInstances(DateTime asAt) + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default) { var now = asAt.ToUniversalTime().Ticks; using (var session = _database.OpenAsyncSession()) @@ -79,20 +80,20 @@ public async Task> GetRunnableInstances(DateTime asAt) && (w.Status == WorkflowStatus.Runnable) ).Select(x => x.Id); - return await l.ToListAsync(); + return await l.ToListAsync(cancellationToken); } } - public async Task GetWorkflowInstance(string Id) + public async Task GetWorkflowInstance(string Id, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { - var result = await session.Query().FirstOrDefaultAsync(x => x.Id == Id); + var result = await session.Query().FirstOrDefaultAsync(x => x.Id == Id, cancellationToken); return result; } } - public async Task> GetWorkflowInstances(IEnumerable ids) + public async Task> GetWorkflowInstances(IEnumerable ids, CancellationToken cancellationToken = default) { if (ids == null) { @@ -102,7 +103,7 @@ public async Task> GetWorkflowInstances(IEnumerabl using (var session = _database.OpenAsyncSession()) { var list = session.Query().Where(x => x.Id.In(ids)); - return await list.ToListAsync(); + return await list.ToListAsync(cancellationToken); } } @@ -128,18 +129,18 @@ public async Task> GetWorkflowInstances(WorkflowSt } } - public async Task CreateEventSubscription(EventSubscription subscription) + public async Task CreateEventSubscription(EventSubscription subscription, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { - await session.StoreAsync(subscription); + await session.StoreAsync(subscription, cancellationToken); var id = subscription.Id; - await session.SaveChangesAsync(); + await session.SaveChangesAsync(cancellationToken); return id; } } - public async Task TerminateSubscription(string eventSubscriptionId) + public async Task TerminateSubscription(string eventSubscriptionId, CancellationToken _ = default) { using (var session = _database.OpenAsyncSession()) { @@ -148,16 +149,16 @@ public async Task TerminateSubscription(string eventSubscriptionId) } } - public async Task GetSubscription(string eventSubscriptionId) + public async Task GetSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { var result = session.Query().Where(x => x.Id == eventSubscriptionId); - return await result.FirstOrDefaultAsync(); + return await result.FirstOrDefaultAsync(cancellationToken); } } - public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { @@ -168,11 +169,11 @@ public async Task GetFirstOpenSubscription(string eventName, && x.ExternalToken == null ); - return await q.FirstOrDefaultAsync(); + return await q.FirstOrDefaultAsync(cancellationToken); } } - public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken cancellationToken = default) { try { @@ -187,7 +188,7 @@ public async Task SetSubscriptionToken(string eventSubscriptionId, string strbuilder.Append($"e.ExternalWorkerId = 'workerId'"); strbuilder.Append("}"); - var operation = await _database.Operations.SendAsync(new PatchByQueryOperation(strbuilder.ToString())); + var operation = await _database.Operations.SendAsync(new PatchByQueryOperation(strbuilder.ToString()), token: cancellationToken); operation.WaitForCompletion(); return true; } @@ -197,7 +198,7 @@ public async Task SetSubscriptionToken(string eventSubscriptionId, string } } - public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken cancellationToken = default) { try { @@ -212,7 +213,7 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke strbuilder.Append($"e.ExternalWorkerId = null"); strbuilder.Append("}"); - var operation = await _database.Operations.SendAsync(new PatchByQueryOperation(strbuilder.ToString())); + var operation = await _database.Operations.SendAsync(new PatchByQueryOperation(strbuilder.ToString()), token: cancellationToken); operation.WaitForCompletion(); } catch (Exception e) @@ -223,7 +224,7 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke public void EnsureStoreExists() { } - public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { @@ -233,31 +234,31 @@ public async Task> GetSubscriptions(string eventN && x.SubscribeAsOf <= asOf ); - return await q.ToListAsync(); + return await q.ToListAsync(cancellationToken); } } - public async Task CreateEvent(Event newEvent) + public async Task CreateEvent(Event newEvent, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { - await session.StoreAsync(newEvent); + await session.StoreAsync(newEvent, cancellationToken); var id = newEvent.Id; - await session.SaveChangesAsync(); + await session.SaveChangesAsync(cancellationToken); return id; } } - public async Task GetEvent(string id) + public async Task GetEvent(string id, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { var result = session.Query().Where(x => x.Id == id); - return await result.FirstAsync(); + return await result.FirstAsync(cancellationToken); } } - public async Task> GetRunnableEvents(DateTime asAt) + public async Task> GetRunnableEvents(DateTime asAt, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { @@ -266,21 +267,21 @@ public async Task> GetRunnableEvents(DateTime asAt) .Where(x => !x.IsProcessed && x.EventTime < now) .Select(x => x.Id); - return await result.ToListAsync(); + return await result.ToListAsync(cancellationToken); } } - public async Task MarkEventProcessed(string id) + public async Task MarkEventProcessed(string id, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { session.Advanced.Patch(id, x => x.IsProcessed, true); - await session.SaveChangesAsync(); + await session.SaveChangesAsync(cancellationToken); } } - public async Task> GetEvents(string eventName, string eventKey, DateTime asOf) + public async Task> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken) { using (var session = _database.OpenAsyncSession()) { @@ -291,25 +292,25 @@ public async Task> GetEvents(string eventName, string eventK && x.EventTime >= asOf) .Select(x => x.Id); - return await q.ToListAsync(); + return await q.ToListAsync(cancellationToken); } } - public async Task MarkEventUnprocessed(string id) + public async Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default) { using (var session = _database.OpenAsyncSession()) { session.Advanced.Patch(id, x => x.IsProcessed, false); - await session.SaveChangesAsync(); + await session.SaveChangesAsync(cancellationToken); } } - public async Task PersistErrors(IEnumerable errors) + public async Task PersistErrors(IEnumerable errors, CancellationToken cancellationToken = default) { if (errors.Any()) { - var blk = _database.BulkInsert(); + var blk = _database.BulkInsert(token: cancellationToken); foreach (var error in errors) await blk.StoreAsync(error); diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index afc531b7a..073d85930 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -10,6 +10,7 @@ using WorkflowCore.Providers.AWS.Interface; using WorkflowCore.Interface; using WorkflowCore.Models; +using System.Threading; namespace WorkflowCore.Providers.AWS.Services { @@ -32,7 +33,7 @@ public DynamoPersistenceProvider(AWSCredentials credentials, AmazonDynamoDBConfi _provisioner = provisioner; } - public async Task CreateNewWorkflow(WorkflowInstance workflow) + public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { workflow.Id = Guid.NewGuid().ToString(); @@ -43,12 +44,12 @@ public async Task CreateNewWorkflow(WorkflowInstance workflow) ConditionExpression = "attribute_not_exists(id)" }; - var response = await _client.PutItemAsync(req); + var _ = await _client.PutItemAsync(req, cancellationToken); return workflow.Id; } - public async Task PersistWorkflow(WorkflowInstance workflow) + public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { var request = new PutItemRequest() { @@ -56,10 +57,10 @@ public async Task PersistWorkflow(WorkflowInstance workflow) Item = workflow.ToDynamoMap() }; - var response = await _client.PutItemAsync(request); + var response = await _client.PutItemAsync(request, cancellationToken); } - public async Task> GetRunnableInstances(DateTime asAt) + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default) { var result = new List(); var now = asAt.ToUniversalTime().Ticks; @@ -88,7 +89,7 @@ public async Task> GetRunnableInstances(DateTime asAt) ScanIndexForward = true }; - var response = await _client.QueryAsync(request); + var response = await _client.QueryAsync(request, cancellationToken); foreach (var item in response.Items) { @@ -103,7 +104,7 @@ public Task> GetWorkflowInstances(WorkflowStatus? throw new NotImplementedException(); } - public async Task GetWorkflowInstance(string Id) + public async Task GetWorkflowInstance(string Id, CancellationToken cancellationToken = default) { var req = new GetItemRequest() { @@ -113,12 +114,12 @@ public async Task GetWorkflowInstance(string Id) { "id", new AttributeValue(Id) } } }; - var response = await _client.GetItemAsync(req); + var response = await _client.GetItemAsync(req, cancellationToken); return response.Item.ToWorkflowInstance(); } - public async Task> GetWorkflowInstances(IEnumerable ids) + public async Task> GetWorkflowInstances(IEnumerable ids, CancellationToken cancellationToken = default) { if (ids == null) { @@ -151,7 +152,7 @@ public async Task> GetWorkflowInstances(IEnumerabl BatchGetItemResponse response; do { - response = await _client.BatchGetItemAsync(request); + response = await _client.BatchGetItemAsync(request, cancellationToken); foreach (var tableResponse in response.Responses) result.AddRange(tableResponse.Value); @@ -161,7 +162,7 @@ public async Task> GetWorkflowInstances(IEnumerabl return result.Select(i => i.ToWorkflowInstance()); } - public async Task CreateEventSubscription(EventSubscription subscription) + public async Task CreateEventSubscription(EventSubscription subscription, CancellationToken cancellationToken = default) { subscription.Id = Guid.NewGuid().ToString(); @@ -172,12 +173,12 @@ public async Task CreateEventSubscription(EventSubscription subscription ConditionExpression = "attribute_not_exists(id)" }; - var response = await _client.PutItemAsync(req); + var response = await _client.PutItemAsync(req, cancellationToken); return subscription.Id; } - public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default) { var result = new List(); var asOfTicks = asOf.ToUniversalTime().Ticks; @@ -203,7 +204,7 @@ public async Task> GetSubscriptions(string eventN ScanIndexForward = true }; - var response = await _client.QueryAsync(request); + var response = await _client.QueryAsync(request, cancellationToken); foreach (var item in response.Items) { @@ -213,7 +214,7 @@ public async Task> GetSubscriptions(string eventN return result; } - public async Task TerminateSubscription(string eventSubscriptionId) + public async Task TerminateSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default) { var request = new DeleteItemRequest() { @@ -223,10 +224,10 @@ public async Task TerminateSubscription(string eventSubscriptionId) { "id", new AttributeValue(eventSubscriptionId) } } }; - await _client.DeleteItemAsync(request); + await _client.DeleteItemAsync(request, cancellationToken); } - public async Task CreateEvent(Event newEvent) + public async Task CreateEvent(Event newEvent, CancellationToken cancellationToken = default) { newEvent.Id = Guid.NewGuid().ToString(); @@ -237,12 +238,12 @@ public async Task CreateEvent(Event newEvent) ConditionExpression = "attribute_not_exists(id)" }; - var response = await _client.PutItemAsync(req); + var _ = await _client.PutItemAsync(req, cancellationToken); return newEvent.Id; } - public async Task GetEvent(string id) + public async Task GetEvent(string id, CancellationToken cancellationToken = default) { var req = new GetItemRequest() { @@ -252,12 +253,12 @@ public async Task GetEvent(string id) { "id", new AttributeValue(id) } } }; - var response = await _client.GetItemAsync(req); + var response = await _client.GetItemAsync(req, cancellationToken); return response.Item.ToEvent(); } - public async Task> GetRunnableEvents(DateTime asAt) + public async Task> GetRunnableEvents(DateTime asAt, CancellationToken cancellationToken = default) { var result = new List(); var now = asAt.ToUniversalTime().Ticks; @@ -281,7 +282,7 @@ public async Task> GetRunnableEvents(DateTime asAt) ScanIndexForward = true }; - var response = await _client.QueryAsync(request); + var response = await _client.QueryAsync(request, cancellationToken); foreach (var item in response.Items) { @@ -291,7 +292,7 @@ public async Task> GetRunnableEvents(DateTime asAt) return result; } - public async Task> GetEvents(string eventName, string eventKey, DateTime asOf) + public async Task> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken) { var result = new List(); var asOfTicks = asOf.ToUniversalTime().Ticks; @@ -317,7 +318,7 @@ public async Task> GetEvents(string eventName, string eventK ScanIndexForward = true }; - var response = await _client.QueryAsync(request); + var response = await _client.QueryAsync(request, cancellationToken); foreach (var item in response.Items) { @@ -327,7 +328,7 @@ public async Task> GetEvents(string eventName, string eventK return result; } - public async Task MarkEventProcessed(string id) + public async Task MarkEventProcessed(string id, CancellationToken cancellationToken = default) { var request = new UpdateItemRequest() { @@ -338,10 +339,10 @@ public async Task MarkEventProcessed(string id) }, UpdateExpression = "REMOVE not_processed" }; - await _client.UpdateItemAsync(request); + await _client.UpdateItemAsync(request, cancellationToken); } - public async Task MarkEventUnprocessed(string id) + public async Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default) { var request = new UpdateItemRequest() { @@ -356,10 +357,10 @@ public async Task MarkEventUnprocessed(string id) { ":n" , new AttributeValue() { N = 1.ToString() } } } }; - await _client.UpdateItemAsync(request); + await _client.UpdateItemAsync(request, cancellationToken); } - public Task PersistErrors(IEnumerable errors) + public Task PersistErrors(IEnumerable errors, CancellationToken _ = default) { //TODO return Task.CompletedTask; @@ -370,7 +371,7 @@ public void EnsureStoreExists() _provisioner.ProvisionTables().Wait(); } - public async Task GetSubscription(string eventSubscriptionId) + public async Task GetSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default) { var req = new GetItemRequest() { @@ -380,12 +381,12 @@ public async Task GetSubscription(string eventSubscriptionId) { "id", new AttributeValue(eventSubscriptionId) } } }; - var response = await _client.GetItemAsync(req); + var response = await _client.GetItemAsync(req, cancellationToken); return response.Item.ToEventSubscription(); } - public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default) { var result = new List(); var asOfTicks = asOf.ToUniversalTime().Ticks; @@ -413,7 +414,7 @@ public async Task GetFirstOpenSubscription(string eventName, ScanIndexForward = true }; - var response = await _client.QueryAsync(request); + var response = await _client.QueryAsync(request, cancellationToken); foreach (var item in response.Items) result.Add(item.ToEventSubscription()); @@ -421,7 +422,7 @@ public async Task GetFirstOpenSubscription(string eventName, return result.FirstOrDefault(); } - public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken cancellationToken = default) { var request = new UpdateItemRequest() { @@ -441,7 +442,7 @@ public async Task SetSubscriptionToken(string eventSubscriptionId, string }; try { - await _client.UpdateItemAsync(request); + await _client.UpdateItemAsync(request, cancellationToken); return true; } catch (ConditionalCheckFailedException) @@ -450,7 +451,7 @@ public async Task SetSubscriptionToken(string eventSubscriptionId, string } } - public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken cancellationToken = default) { var request = new UpdateItemRequest() { @@ -467,7 +468,7 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke } }; - await _client.UpdateItemAsync(request); + await _client.UpdateItemAsync(request, cancellationToken); } } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs index 8ca697fc2..0b2707994 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos; +using Microsoft.Azure.Cosmos.Linq; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Providers.Azure.Interface; @@ -13,7 +14,6 @@ namespace WorkflowCore.Providers.Azure.Services { public class CosmosDbPersistenceProvider : IPersistenceProvider { - public const string WorkflowContainerName = "workflows"; public const string EventContainerName = "events"; public const string SubscriptionContainerName = "subscriptions"; @@ -35,7 +35,7 @@ public CosmosDbPersistenceProvider(string connectionString, string dbId, ICosmos _subscriptionContainer = new Lazy(() => _client.GetDatabase(_dbId).GetContainer(SubscriptionContainerName)); } - public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken cancellationToken = default) { var existing = await _subscriptionContainer.Value.ReadItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId)); @@ -45,27 +45,27 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke existing.Resource.ExternalWorkerId = null; existing.Resource.ExternalTokenExpiry = null; - await _subscriptionContainer.Value.ReplaceItemAsync(existing.Resource, eventSubscriptionId); + await _subscriptionContainer.Value.ReplaceItemAsync(existing.Resource, eventSubscriptionId, cancellationToken: cancellationToken); } - public async Task CreateEvent(Event newEvent) + public async Task CreateEvent(Event newEvent, CancellationToken cancellationToken) { newEvent.Id = Guid.NewGuid().ToString(); - var result = await _eventContainer.Value.CreateItemAsync(PersistedEvent.FromInstance(newEvent)); + var result = await _eventContainer.Value.CreateItemAsync(PersistedEvent.FromInstance(newEvent), cancellationToken: cancellationToken); return result.Resource.id; } - public async Task CreateEventSubscription(EventSubscription subscription) + public async Task CreateEventSubscription(EventSubscription subscription, CancellationToken cancellationToken) { subscription.Id = Guid.NewGuid().ToString(); - var result = await _subscriptionContainer.Value.CreateItemAsync(PersistedSubscription.FromInstance(subscription)); + var result = await _subscriptionContainer.Value.CreateItemAsync(PersistedSubscription.FromInstance(subscription), cancellationToken: cancellationToken); return result.Resource.id; } - public async Task CreateNewWorkflow(WorkflowInstance workflow) + public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken) { workflow.Id = Guid.NewGuid().ToString(); - var result = await _workflowContainer.Value.CreateItemAsync(PersistedWorkflow.FromInstance(workflow)); + var result = await _workflowContainer.Value.CreateItemAsync(PersistedWorkflow.FromInstance(workflow), cancellationToken: cancellationToken); return result.Resource.id; } @@ -74,69 +74,120 @@ public void EnsureStoreExists() _provisioner.Provision(_dbId).Wait(); } - public async Task GetEvent(string id) + public async Task GetEvent(string id, CancellationToken cancellationToken) { - var resp = await _eventContainer.Value.ReadItemAsync(id, new PartitionKey(id)); + var resp = await _eventContainer.Value.ReadItemAsync(id, new PartitionKey(id), cancellationToken: cancellationToken); return PersistedEvent.ToInstance(resp.Resource); } - public Task> GetEvents(string eventName, string eventKey, DateTime asOf) + public async Task> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken) { - var data = _eventContainer.Value.GetItemLinqQueryable(true) - .Where(x => x.EventName == eventName && x.EventKey == eventKey) - .Where(x => x.EventTime >= asOf) - .Select(x => x.id); - - return Task.FromResult(data.AsEnumerable()); + var events = new List(); + using (FeedIterator feedIterator = _eventContainer.Value.GetItemLinqQueryable() + .Where(x => x.EventName == eventName && x.EventKey == eventKey) + .Where(x => x.EventTime >= asOf) + .ToFeedIterator()) + { + while (feedIterator.HasMoreResults) + { + foreach (var item in await feedIterator.ReadNextAsync(cancellationToken)) + { + events.Add(item.id); + } + } + } + + return events; } - public Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken) { - var data = _subscriptionContainer.Value.GetItemLinqQueryable(true) - .FirstOrDefault(x => x.ExternalToken == null && x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf); - - return Task.FromResult(PersistedSubscription.ToInstance(data)); + EventSubscription eventSubscription = null; + using (FeedIterator feedIterator = _subscriptionContainer.Value.GetItemLinqQueryable() + .Where(x => x.ExternalToken == null && x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf) + .ToFeedIterator()) + { + while (feedIterator.HasMoreResults && eventSubscription == null) + { + foreach (var item in await feedIterator.ReadNextAsync(cancellationToken)) + { + eventSubscription = PersistedSubscription.ToInstance(item); + } + } + } + + return eventSubscription; } - public Task> GetRunnableEvents(DateTime asAt) + public async Task> GetRunnableEvents(DateTime asAt, CancellationToken cancellationToken) { - var data = _eventContainer.Value.GetItemLinqQueryable(true) - .Where(x => !x.IsProcessed) - .Where(x => x.EventTime <= asAt.ToUniversalTime()) - .Select(x => x.id); - - return Task.FromResult(data.AsEnumerable()); + var events = new List(); + using (FeedIterator feedIterator = _eventContainer.Value.GetItemLinqQueryable() + .Where(x => !x.IsProcessed) + .Where(x => x.EventTime <= asAt.ToUniversalTime()) + .ToFeedIterator()) + { + while (feedIterator.HasMoreResults) + { + foreach (var item in await feedIterator.ReadNextAsync(cancellationToken)) + { + events.Add(item.id); + } + } + } + + return events; } - public Task> GetRunnableInstances(DateTime asAt) + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken) { var now = asAt.ToUniversalTime().Ticks; - var data = _workflowContainer.Value.GetItemLinqQueryable(true) - .Where(x => x.NextExecution.HasValue && (x.NextExecution <= now) && (x.Status == WorkflowStatus.Runnable)) - .Select(x => x.id); + var instances = new List(); + using (FeedIterator feedIterator = _workflowContainer.Value.GetItemLinqQueryable() + .Where(x => x.NextExecution.HasValue && (x.NextExecution <= now) && (x.Status == WorkflowStatus.Runnable)) + .ToFeedIterator()) + { + while (feedIterator.HasMoreResults) + { + foreach (var item in await feedIterator.ReadNextAsync(cancellationToken)) + { + instances.Add(item.id); + } + } + } - return Task.FromResult(data.AsEnumerable()); + return instances; } - public async Task GetSubscription(string eventSubscriptionId) + public async Task GetSubscription(string eventSubscriptionId, CancellationToken cancellationToken) { - var resp = await _subscriptionContainer.Value.ReadItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId)); + var resp = await _subscriptionContainer.Value.ReadItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId), cancellationToken: cancellationToken); return PersistedSubscription.ToInstance(resp.Resource); } - public Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken) { - var data = _subscriptionContainer.Value.GetItemLinqQueryable(true) + var subscriptions = new List(); + using (FeedIterator feedIterator = _subscriptionContainer.Value.GetItemLinqQueryable() .Where(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf) - .ToList() - .Select(x => PersistedSubscription.ToInstance(x)); - return Task.FromResult(data.AsEnumerable()); + .ToFeedIterator()) + { + while (feedIterator.HasMoreResults) + { + foreach (var item in await feedIterator.ReadNextAsync(cancellationToken)) + { + subscriptions.Add(PersistedSubscription.ToInstance(item)); + } + } + } + + return subscriptions; } - public async Task GetWorkflowInstance(string Id) + public async Task GetWorkflowInstance(string Id, CancellationToken cancellationToken) { - var result = await _workflowContainer.Value.ReadItemAsync(Id, new PartitionKey(Id)); + var result = await _workflowContainer.Value.ReadItemAsync(Id, new PartitionKey(Id), cancellationToken: cancellationToken); return PersistedWorkflow.ToInstance(result.Resource); } @@ -145,51 +196,51 @@ public Task> GetWorkflowInstances(WorkflowStatus? throw new NotImplementedException(); } - public Task> GetWorkflowInstances(IEnumerable ids) + public Task> GetWorkflowInstances(IEnumerable ids, CancellationToken cancellationToken) { throw new NotImplementedException(); } - public async Task MarkEventProcessed(string id) + public async Task MarkEventProcessed(string id, CancellationToken cancellationToken) { - var evt = await _eventContainer.Value.ReadItemAsync(id, new PartitionKey(id)); + var evt = await _eventContainer.Value.ReadItemAsync(id, new PartitionKey(id), cancellationToken: cancellationToken); evt.Resource.IsProcessed = true; - await _eventContainer.Value.ReplaceItemAsync(evt.Resource, id); + await _eventContainer.Value.ReplaceItemAsync(evt.Resource, id, cancellationToken: cancellationToken); } - public async Task MarkEventUnprocessed(string id) + public async Task MarkEventUnprocessed(string id, CancellationToken cancellationToken) { - var evt = await _eventContainer.Value.ReadItemAsync(id, new PartitionKey(id)); + var evt = await _eventContainer.Value.ReadItemAsync(id, new PartitionKey(id), cancellationToken: cancellationToken); evt.Resource.IsProcessed = false; - await _eventContainer.Value.ReplaceItemAsync(evt.Resource, id); + await _eventContainer.Value.ReplaceItemAsync(evt.Resource, id, cancellationToken: cancellationToken); } - public Task PersistErrors(IEnumerable errors) + public Task PersistErrors(IEnumerable errors, CancellationToken _ = default) { return Task.CompletedTask; } - public async Task PersistWorkflow(WorkflowInstance workflow) + public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken) { - await _workflowContainer.Value.UpsertItemAsync(PersistedWorkflow.FromInstance(workflow)); + await _workflowContainer.Value.UpsertItemAsync(PersistedWorkflow.FromInstance(workflow), cancellationToken: cancellationToken); } - public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken cancellationToken) { - var sub = await _subscriptionContainer.Value.ReadItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId)); + var sub = await _subscriptionContainer.Value.ReadItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId), cancellationToken: cancellationToken); var existingEntity = sub.Resource; existingEntity.ExternalToken = token; existingEntity.ExternalWorkerId = workerId; existingEntity.ExternalTokenExpiry = expiry; - await _subscriptionContainer.Value.ReplaceItemAsync(existingEntity, eventSubscriptionId); + await _subscriptionContainer.Value.ReplaceItemAsync(existingEntity, eventSubscriptionId, cancellationToken: cancellationToken); return true; } - public async Task TerminateSubscription(string eventSubscriptionId) + public async Task TerminateSubscription(string eventSubscriptionId, CancellationToken cancellationToken) { - await _subscriptionContainer.Value.DeleteItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId)); + await _subscriptionContainer.Value.DeleteItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId), cancellationToken: cancellationToken); } } } diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index 3e593a550..616071550 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Newtonsoft.Json; @@ -37,14 +38,14 @@ public RedisPersistenceProvider(string connectionString, string prefix, bool rem _removeComplete = removeComplete; } - public async Task CreateNewWorkflow(WorkflowInstance workflow) + public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken _ = default) { workflow.Id = Guid.NewGuid().ToString(); await PersistWorkflow(workflow); return workflow.Id; } - public async Task PersistWorkflow(WorkflowInstance workflow) + public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken _ = default) { var str = JsonConvert.SerializeObject(workflow, _serializerSettings); await _redis.HashSetAsync($"{_prefix}.{WORKFLOW_SET}", workflow.Id, str); @@ -59,7 +60,7 @@ public async Task PersistWorkflow(WorkflowInstance workflow) } } - public async Task> GetRunnableInstances(DateTime asAt) + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken _ = default) { var result = new List(); var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{WORKFLOW_SET}.{RUNNABLE_INDEX}", -1, asAt.ToUniversalTime().Ticks); @@ -76,13 +77,13 @@ public Task> GetWorkflowInstances(WorkflowStatus? throw new NotImplementedException(); } - public async Task GetWorkflowInstance(string Id) + public async Task GetWorkflowInstance(string Id, CancellationToken _ = default) { var raw = await _redis.HashGetAsync($"{_prefix}.{WORKFLOW_SET}", Id); return JsonConvert.DeserializeObject(raw, _serializerSettings); } - public async Task> GetWorkflowInstances(IEnumerable ids) + public async Task> GetWorkflowInstances(IEnumerable ids, CancellationToken _ = default) { if (ids == null) { @@ -93,7 +94,7 @@ public async Task> GetWorkflowInstances(IEnumerabl return raw.Select(r => JsonConvert.DeserializeObject(r, _serializerSettings)); } - public async Task CreateEventSubscription(EventSubscription subscription) + public async Task CreateEventSubscription(EventSubscription subscription, CancellationToken _ = default) { subscription.Id = Guid.NewGuid().ToString(); var str = JsonConvert.SerializeObject(subscription, _serializerSettings); @@ -103,7 +104,7 @@ public async Task CreateEventSubscription(EventSubscription subscription return subscription.Id; } - public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf) + public async Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken _ = default) { var result = new List(); var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{SUBSCRIPTION_SET}.{EVENTSLUG_INDEX}.{eventName}-{eventKey}", -1, asOf.Ticks); @@ -118,7 +119,7 @@ public async Task> GetSubscriptions(string eventN return result; } - public async Task TerminateSubscription(string eventSubscriptionId) + public async Task TerminateSubscription(string eventSubscriptionId, CancellationToken _ = default) { var existingRaw = await _redis.HashGetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId); var existing = JsonConvert.DeserializeObject(existingRaw, _serializerSettings); @@ -126,18 +127,18 @@ public async Task TerminateSubscription(string eventSubscriptionId) await _redis.SortedSetRemoveAsync($"{_prefix}.{SUBSCRIPTION_SET}.{EVENTSLUG_INDEX}.{existing.EventName}-{existing.EventKey}", eventSubscriptionId); } - public async Task GetSubscription(string eventSubscriptionId) + public async Task GetSubscription(string eventSubscriptionId, CancellationToken _ = default) { var raw = await _redis.HashGetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId); return JsonConvert.DeserializeObject(raw, _serializerSettings); } - public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf) + public async Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default) { - return (await GetSubscriptions(eventName, eventKey, asOf)).FirstOrDefault(sub => string.IsNullOrEmpty(sub.ExternalToken)); + return (await GetSubscriptions(eventName, eventKey, asOf, cancellationToken)).FirstOrDefault(sub => string.IsNullOrEmpty(sub.ExternalToken)); } - public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry) + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken _ = default) { var item = JsonConvert.DeserializeObject(await _redis.HashGetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId), _serializerSettings); if (item.ExternalToken != null) @@ -150,7 +151,7 @@ public async Task SetSubscriptionToken(string eventSubscriptionId, string return true; } - public async Task ClearSubscriptionToken(string eventSubscriptionId, string token) + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken _ = default) { var item = JsonConvert.DeserializeObject(await _redis.HashGetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId), _serializerSettings); if (item.ExternalToken != token) @@ -162,7 +163,7 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke await _redis.HashSetAsync($"{_prefix}.{SUBSCRIPTION_SET}", eventSubscriptionId, str); } - public async Task CreateEvent(Event newEvent) + public async Task CreateEvent(Event newEvent, CancellationToken _ = default) { newEvent.Id = Guid.NewGuid().ToString(); var str = JsonConvert.SerializeObject(newEvent, _serializerSettings); @@ -177,13 +178,13 @@ public async Task CreateEvent(Event newEvent) return newEvent.Id; } - public async Task GetEvent(string id) + public async Task GetEvent(string id, CancellationToken _ = default) { var raw = await _redis.HashGetAsync($"{_prefix}.{EVENT_SET}", id); return JsonConvert.DeserializeObject(raw, _serializerSettings); } - public async Task> GetRunnableEvents(DateTime asAt) + public async Task> GetRunnableEvents(DateTime asAt, CancellationToken _ = default) { var result = new List(); var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{EVENT_SET}.{RUNNABLE_INDEX}", -1, asAt.Ticks); @@ -194,7 +195,7 @@ public async Task> GetRunnableEvents(DateTime asAt) return result; } - public async Task> GetEvents(string eventName, string eventKey, DateTime asOf) + public async Task> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken _ = default) { var result = new List(); var data = await _redis.SortedSetRangeByScoreAsync($"{_prefix}.{EVENT_SET}.{EVENTSLUG_INDEX}.{eventName}-{eventKey}", asOf.Ticks); @@ -205,25 +206,25 @@ public async Task> GetEvents(string eventName, string eventK return result; } - public async Task MarkEventProcessed(string id) + public async Task MarkEventProcessed(string id, CancellationToken cancellationToken = default) { - var evt = await GetEvent(id); + var evt = await GetEvent(id, cancellationToken); evt.IsProcessed = true; var str = JsonConvert.SerializeObject(evt, _serializerSettings); await _redis.HashSetAsync($"{_prefix}.{EVENT_SET}", evt.Id, str); await _redis.SortedSetRemoveAsync($"{_prefix}.{EVENT_SET}.{RUNNABLE_INDEX}", id); } - public async Task MarkEventUnprocessed(string id) + public async Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default) { - var evt = await GetEvent(id); + var evt = await GetEvent(id, cancellationToken); evt.IsProcessed = false; var str = JsonConvert.SerializeObject(evt, _serializerSettings); await _redis.HashSetAsync($"{_prefix}.{EVENT_SET}", evt.Id, str); await _redis.SortedSetAddAsync($"{_prefix}.{EVENT_SET}.{RUNNABLE_INDEX}", evt.Id, evt.EventTime.Ticks); } - public Task PersistErrors(IEnumerable errors) + public Task PersistErrors(IEnumerable errors, CancellationToken _ = default) { return Task.CompletedTask; } From ebb168996e9fbcea3be54e091312edb1529eb242 Mon Sep 17 00:00:00 2001 From: Bruce Zlata Date: Sun, 25 Apr 2021 14:23:09 +0800 Subject: [PATCH 327/462] increment the minor version number --- .../WorkflowCore.Providers.Azure.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj index cd09e101e..b2f20ddc9 100644 --- a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj +++ b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj @@ -7,15 +7,15 @@ - Provides distributed lock management on Workflow Core - Provides Queueing support on Workflow Core workflow workflowcore dlm - 3.1.0 + 3.2.0 $(PackageTargetFallback);dnxcore50 https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git Daniel Gerlag - 3.1.0.0 - 3.1.0.0 + 3.2.0.0 + 3.2.0.0 From b84c9cef62aa1b496196d117bf731fbdfe209a80 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 8 May 2021 17:01:42 -0700 Subject: [PATCH 328/462] harmonize versions --- src/Directory.Build.props | 13 +++++++++++++ src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 4 ---- src/WorkflowCore/WorkflowCore.csproj | 10 ---------- .../WorkflowCore.Users/WorkflowCore.Users.csproj | 3 --- .../WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj | 1 - .../WorkflowCore.LockProviders.SqlServer.csproj | 1 - .../WorkflowCore.Persistence.EntityFramework.csproj | 4 ---- .../WorkflowCore.Persistence.MongoDB.csproj | 4 ---- .../WorkflowCore.Persistence.MySQL.csproj | 3 --- .../WorkflowCore.Persistence.PostgreSQL.csproj | 4 ---- .../WorkflowCore.Persistence.SqlServer.csproj | 4 ---- .../WorkflowCore.Persistence.Sqlite.csproj | 4 ---- .../WorkflowCore.Providers.AWS.csproj | 3 --- .../WorkflowCore.Providers.Azure.csproj | 3 --- .../WorkflowCore.Providers.Elasticsearch.csproj | 1 - .../WorkflowCore.Providers.Redis.csproj | 5 +---- .../WorkflowCore.QueueProviders.RabbitMQ.csproj | 5 ----- .../WorkflowCore.QueueProviders.SqlServer.csproj | 2 +- 18 files changed, 15 insertions(+), 59 deletions(-) create mode 100644 src/Directory.Build.props diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 000000000..f8f086812 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,13 @@ + + + https://github.com/danielgerlag/workflow-core + https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md + git + https://github.com/danielgerlag/workflow-core.git + 3.5.0 + 3.5.0.0 + 3.5.0.0 + https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png + 3.5.0 + + diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index 689d95119..230286071 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -2,14 +2,10 @@ netstandard2.0 - 3.3.1 DSL extenstion for Workflow Core provding support for JSON and YAML workflow definitions. Daniel Gerlag WorkflowCore - https://github.com/danielgerlag/workflow-core - https://github.com/danielgerlag/workflow-core.git - git diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index a439d3bac..1457092bd 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -7,20 +7,10 @@ WorkflowCore WorkflowCore workflow;.NET;Core;state machine - https://github.com/danielgerlag/workflow-core - https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md - git - https://github.com/danielgerlag/workflow-core.git false false false Workflow Core is a light weight workflow engine targeting .NET Standard. - 3.4.0 - 3.4.0.0 - 3.4.0.0 - - https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.4.0 diff --git a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj index 5a8c0beab..66c96f4ec 100644 --- a/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj +++ b/src/extensions/WorkflowCore.Users/WorkflowCore.Users.csproj @@ -15,9 +15,6 @@ false false Provides extensions for Workflow Core to enable human workflows. - 2.1.1 - 2.1.1.0 - 2.1.1.0 diff --git a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj index a3fb9bbb7..f6eb6ea92 100644 --- a/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj +++ b/src/extensions/WorkflowCore.WebAPI/WorkflowCore.WebAPI.csproj @@ -14,7 +14,6 @@ false false false - 2.0.0 WebAPI wrapper for Workflow host diff --git a/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj b/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj index ceb51046d..77fbe950d 100644 --- a/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj @@ -5,7 +5,6 @@ Distributed lock provider for Workflow-core using SQL Server https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md - 2.0.0 diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index b126a9c23..39627c7c9 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -14,11 +14,7 @@ false false false - 3.1.1 Base package for Workflow-core peristence providers using entity framework - 3.1.1.0 - 3.1.1.0 - 3.1.1 diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index b365d33f6..13df8d4aa 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -14,11 +14,7 @@ false false false - 3.0.4 Provides support to persist workflows running on Workflow Core to a MongoDB database. - 3.0.4.0 - 3.0.4.0 - 3.0.4 diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index a031e5fba..af6cbc18d 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -16,9 +16,6 @@ false false Provides support to persist workflows running on Workflow Core to a MySQL database. - 3.0.1 - 3.0.1.0 - 3.0.1.0 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index bcebe0247..3aa405620 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -15,10 +15,6 @@ false false Provides support to persist workflows running on Workflow Core to a PostgreSQL database. - 3.3.0 - 3.3.0.0 - 3.3.0.0 - 3.3.0 diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index f352e79c0..38375e8e6 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -15,11 +15,7 @@ false false false - 3.3.0 Provides support to persist workflows running on Workflow Core to a SQL Server database. - 3.3.0.0 - 3.3.0.0 - 3.3.0 diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index faa1eb934..d4c73e037 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -16,10 +16,6 @@ false false Provides support to persist workflows running on Workflow Core to a Sqlite database. - 3.3.0 - 3.3.0.0 - 3.3.0.0 - 3.3.0 diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 84eb6cb74..49b7c2916 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -11,9 +11,6 @@ https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git git - 3.0.2 - 3.0.2.0 - 3.0.2 diff --git a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj index b2f20ddc9..dc7e5609a 100644 --- a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj +++ b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj @@ -7,15 +7,12 @@ - Provides distributed lock management on Workflow Core - Provides Queueing support on Workflow Core workflow workflowcore dlm - 3.2.0 $(PackageTargetFallback);dnxcore50 https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git Daniel Gerlag - 3.2.0.0 - 3.2.0.0 diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index e4263abaf..db78bebb7 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -2,7 +2,6 @@ netstandard2.0 - 3.0.0 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core https://github.com/danielgerlag/workflow-core.git diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index bdb8c6456..e35b091ff 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -2,14 +2,11 @@ netstandard2.0 - 3.0.2 https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md https://github.com/danielgerlag/workflow-core.git git https://github.com/danielgerlag/workflow-core - Redis providers for Workflow Core (Persistence, queueing, distributed locking and event hubs) - - 3.0.2 + Redis providers for Workflow Core (Persistence, queueing, distributed locking and event hubs) diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj index 025e83913..48041387f 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj @@ -2,7 +2,6 @@ Workflow Core RabbitMQ queue provider - 1.1.0 Daniel Gerlag netstandard2.0 WorkflowCore.QueueProviders.RabbitMQ @@ -15,11 +14,7 @@ false false false - 2.0.0 Queue provider for Workflow-core using RabbitMQ - 2.0.0.0 - 2.0.0.0 - 2.1.0 diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 36d7cda77..77d5e2421 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -5,7 +5,7 @@ Roberto Paterlini Queue provider for Workflow-core using SQL Server Service Broker - 1.0.3-alpha + alpha From acfb7b968d1c1a55ecb49d607fac93958922ab14 Mon Sep 17 00:00:00 2001 From: Wangyang Ding Date: Mon, 17 May 2021 18:10:16 +0800 Subject: [PATCH 329/462] support deep data transmission --- .../Services/DefinitionLoader.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index b057b223a..0be4618c5 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -217,13 +217,22 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { stepParameter }, typeof(object), output.Value); var dataParameter = Expression.Parameter(dataType, "data"); - Expression targetProperty; + Expression targetProperty = dataParameter; // Check if our datatype has a matching property - var propertyInfo = dataType.GetProperty(output.Key); - if (propertyInfo != null) + + PropertyInfo propertyInfo = null; + String[] paths = output.Key.Split('.'); + foreach(String propertyName in paths) { - targetProperty = Expression.Property(dataParameter, propertyInfo); + if(targetProperty!=null) + { + targetProperty = Expression.Property(targetProperty, propertyName); + } + } + + if (targetProperty != null) + { var targetExpr = Expression.Lambda(targetProperty, dataParameter); step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr)); } From c47d7754f1b7d0482d1497ff34c1db65652176d3 Mon Sep 17 00:00:00 2001 From: Wangyang Ding Date: Mon, 17 May 2021 18:13:58 +0800 Subject: [PATCH 330/462] support deep data transmission --- src/WorkflowCore.DSL/Services/DefinitionLoader.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index 0be4618c5..a4bb5dc06 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -228,6 +228,9 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo if(targetProperty!=null) { targetProperty = Expression.Property(targetProperty, propertyName); + }else + { + break; } } From adc90cbbd12427eee379117ea474e5733580d405 Mon Sep 17 00:00:00 2001 From: Wangyang Ding Date: Wed, 26 May 2021 17:37:23 +0800 Subject: [PATCH 331/462] add indexed properties support --- WorkflowCore.sln | 9 ++- .../Services/DefinitionLoader.cs | 67 ++++++++++++++++--- .../DictionaryData.yml | 10 +++ .../HelloWorld.yml | 24 +++++++ .../MyDataClass.cs | 21 ++++++ .../Steps/HelloWorld.cs | 17 +++++ .../WorkflowCore.Tests.YmalDefinition.csproj | 45 +++++++++++++ .../YmalDefinitionTest.cs | 46 +++++++++++++ 8 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 test/WorkflowCore.Tests.YmalDefinition/DictionaryData.yml create mode 100644 test/WorkflowCore.Tests.YmalDefinition/HelloWorld.yml create mode 100644 test/WorkflowCore.Tests.YmalDefinition/MyDataClass.cs create mode 100644 test/WorkflowCore.Tests.YmalDefinition/Steps/HelloWorld.cs create mode 100644 test/WorkflowCore.Tests.YmalDefinition/WorkflowCore.Tests.YmalDefinition.csproj create mode 100644 test/WorkflowCore.Tests.YmalDefinition/YmalDefinitionTest.cs diff --git a/WorkflowCore.sln b/WorkflowCore.sln index be18fd721..b80b2e757 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -150,7 +150,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.QueuePro EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample19", "src\samples\WorkflowCore.Sample19\WorkflowCore.Sample19.csproj", "{1223ED47-3E5E-4960-B70D-DFAF550F6666}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.YmalDefinition", "test\WorkflowCore.Tests.YmalDefinition\WorkflowCore.Tests.YmalDefinition.csproj", "{099259BB-ABCD-4C7C-AFAE-56A2730B9309}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -374,6 +376,10 @@ Global {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Debug|Any CPU.Build.0 = Debug|Any CPU {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.Build.0 = Release|Any CPU + {099259BB-ABCD-4C7C-AFAE-56A2730B9309}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {099259BB-ABCD-4C7C-AFAE-56A2730B9309}.Debug|Any CPU.Build.0 = Debug|Any CPU + {099259BB-ABCD-4C7C-AFAE-56A2730B9309}.Release|Any CPU.ActiveCfg = Release|Any CPU + {099259BB-ABCD-4C7C-AFAE-56A2730B9309}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -436,6 +442,7 @@ Global {54DE20BA-EBA7-4BF0-9BD9-F03766849716} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {1223ED47-3E5E-4960-B70D-DFAF550F6666} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {AF205715-C8B7-42EF-BF14-AFC9E7F27242} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} + {099259BB-ABCD-4C7C-AFAE-56A2730B9309} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index a4bb5dc06..23546b99a 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -223,23 +223,74 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo PropertyInfo propertyInfo = null; String[] paths = output.Key.Split('.'); - foreach(String propertyName in paths) + + bool hasAddOutput = false; + + foreach (String propertyName in paths) { - if(targetProperty!=null) + if (hasAddOutput) { - targetProperty = Expression.Property(targetProperty, propertyName); - }else + throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); + } + + if (targetProperty == null) { break; + } + + if (propertyName.Contains("[")) + { + String[] items = propertyName.Split('['); + + if (items.Length != 2) + { + throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); + } + + items[1] = items[1].Trim().TrimEnd(']').Trim().Trim('"'); + + MemberExpression memberExpression = Expression.Property(targetProperty, items[0]); + + if (memberExpression == null) + { + throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); + } + propertyInfo = (PropertyInfo)memberExpression.Member; + var propertyType = propertyInfo.PropertyType.GetProperty("Item"); + + propertyInfo = propertyInfo.PropertyType.GetProperty("Item"); + + Action acn = (pStep, pData) => + { + var targetExpr = Expression.Lambda(memberExpression, dataParameter); + object data = targetExpr.Compile().DynamicInvoke(pData); + object resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); ; + propertyInfo.SetValue(data, resolvedValue, new object[] { items[1] }); + }; + + step.Outputs.Add(new ActionParameter(acn)); + + hasAddOutput = true; + } + else + { + try + { + targetProperty = Expression.Property(targetProperty, propertyName); + }catch + { + targetProperty = null; + break; + } } } - - if (targetProperty != null) - { + + if (targetProperty != null && ! hasAddOutput) + { var targetExpr = Expression.Lambda(targetProperty, dataParameter); step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr)); } - else + else if (! hasAddOutput) { // If we did not find a matching property try to find a Indexer with string parameter propertyInfo = dataType.GetProperty("Item"); diff --git a/test/WorkflowCore.Tests.YmalDefinition/DictionaryData.yml b/test/WorkflowCore.Tests.YmalDefinition/DictionaryData.yml new file mode 100644 index 000000000..9e8771448 --- /dev/null +++ b/test/WorkflowCore.Tests.YmalDefinition/DictionaryData.yml @@ -0,0 +1,10 @@ +Id: DictionaryData +Version: 1 +DataType: System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.Int32, System.Private.CoreLib]], System.Private.CoreLib +Steps: +- Id: HelloWorld1 + Name: HelloWorld1 + StepType: WorkflowCore.Tests.YmalDefinition.Steps.HelloWorld, WorkflowCore.Tests.YmalDefinition + Outputs: + Value1: 10 + Value2: 11 \ No newline at end of file diff --git a/test/WorkflowCore.Tests.YmalDefinition/HelloWorld.yml b/test/WorkflowCore.Tests.YmalDefinition/HelloWorld.yml new file mode 100644 index 000000000..1ad77d12e --- /dev/null +++ b/test/WorkflowCore.Tests.YmalDefinition/HelloWorld.yml @@ -0,0 +1,24 @@ +Id: HelloWorld +Version: 1 +DataType: WorkflowCore.Tests.YmalDefinition.MyDataClass, WorkflowCore.Tests.YmalDefinition +Steps: +- Id: HelloWorld1 + Name: HelloWorld1 + StepType: WorkflowCore.Tests.YmalDefinition.Steps.HelloWorld, WorkflowCore.Tests.YmalDefinition + Outputs: + anotherData.Value3: 100121 + Value1: 10 + Value2: 11 + NextStepId: HelloWorld2 +- Id: HelloWorld2 + Name: HelloWorld2 + StepType: WorkflowCore.Tests.YmalDefinition.Steps.HelloWorld, WorkflowCore.Tests.YmalDefinition + Inputs: + Value: data.anotherData.Value3 + Outputs: + Dict["testKey1"]: 100 + Dict["testKey"]: 101 + NextStepId: HelloWorld3 +- Id: HelloWorld3 + Name: HelloWorld3 + StepType: WorkflowCore.Tests.YmalDefinition.Steps.HelloWorld, WorkflowCore.Tests.YmalDefinition diff --git a/test/WorkflowCore.Tests.YmalDefinition/MyDataClass.cs b/test/WorkflowCore.Tests.YmalDefinition/MyDataClass.cs new file mode 100644 index 000000000..fc78e0c3b --- /dev/null +++ b/test/WorkflowCore.Tests.YmalDefinition/MyDataClass.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace WorkflowCore.Tests.YmalDefinition +{ + public class MyDataClass + { + public int Value1 { get; set; } + + public int Value2 { get; set; } + + public Dictionary Dict { get; set; } + + public AnotherDataClass anotherData { get; set; } + } + public class AnotherDataClass + { + public int Value3 { get; set; } + } +} diff --git a/test/WorkflowCore.Tests.YmalDefinition/Steps/HelloWorld.cs b/test/WorkflowCore.Tests.YmalDefinition/Steps/HelloWorld.cs new file mode 100644 index 000000000..9b522e6f0 --- /dev/null +++ b/test/WorkflowCore.Tests.YmalDefinition/Steps/HelloWorld.cs @@ -0,0 +1,17 @@ +using System; +using System.Linq; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Tests.YmalDefinition.Steps +{ + public class HelloWorld : StepBody + { + public int Value { get; set; } = 10; + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Value is " + Value); + return ExecutionResult.Next(); + } + } +} diff --git a/test/WorkflowCore.Tests.YmalDefinition/WorkflowCore.Tests.YmalDefinition.csproj b/test/WorkflowCore.Tests.YmalDefinition/WorkflowCore.Tests.YmalDefinition.csproj new file mode 100644 index 000000000..f948eb349 --- /dev/null +++ b/test/WorkflowCore.Tests.YmalDefinition/WorkflowCore.Tests.YmalDefinition.csproj @@ -0,0 +1,45 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + + + HelloWorld.yml + True + True + + + True + True + HelloWorld.yml + + + + + + Always + + + Always + + + + diff --git a/test/WorkflowCore.Tests.YmalDefinition/YmalDefinitionTest.cs b/test/WorkflowCore.Tests.YmalDefinition/YmalDefinitionTest.cs new file mode 100644 index 000000000..8b5533492 --- /dev/null +++ b/test/WorkflowCore.Tests.YmalDefinition/YmalDefinitionTest.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using WorkflowCore.Interface; +using WorkflowCore.Testing; +using Xunit; + +namespace WorkflowCore.Tests.YmalDefinition +{ + public class YmalDefinitionTest:YamlWorkflowTest + { + + [Fact] + public void LoadWorkflowDefinitions_NoException() + { + Setup(); + + MyDataClass myData = new MyDataClass() { + Value1 = 1, + Value2 = 2, + Dict = new Dictionary(), + anotherData = new AnotherDataClass() { Value3 = 3 } + }; + myData.Dict["testKey"] = 5; + + String workflowInstanceId=StartWorkflow(File.ReadAllText("HelloWorld.yml"), myData); + WaitForWorkflowToComplete(workflowInstanceId, TimeSpan.FromSeconds(30)); + MyDataClass resultData = GetData(workflowInstanceId); + Assert.Equal(10, resultData.Value1); + Assert.Equal(11, resultData.Value2); + Assert.Equal(100121, resultData.anotherData.Value3); + Assert.Equal(100, resultData.Dict["testKey1"]); + Assert.Equal(101, resultData.Dict["testKey"]); + + workflowInstanceId = StartWorkflow(File.ReadAllText("DictionaryData.yml"), new Dictionary()); + WaitForWorkflowToComplete(workflowInstanceId, TimeSpan.FromSeconds(30)); + Dictionary resultData2 = GetData>(workflowInstanceId); + Assert.Equal(10, resultData2["Value1"]); + Assert.Equal(11, resultData2["Value2"]); + } + } +} From 3679769fc7f374e43f4a42783ce339d4c19a0f9a Mon Sep 17 00:00:00 2001 From: Wangyang Ding Date: Wed, 26 May 2021 17:41:44 +0800 Subject: [PATCH 332/462] add indexed properties support --- src/WorkflowCore.DSL/Services/DefinitionLoader.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index 23546b99a..ca50cffd8 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -255,10 +255,7 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo { throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); } - propertyInfo = (PropertyInfo)memberExpression.Member; - var propertyType = propertyInfo.PropertyType.GetProperty("Item"); - - propertyInfo = propertyInfo.PropertyType.GetProperty("Item"); + propertyInfo = ((PropertyInfo)memberExpression.Member).PropertyType.GetProperty("Item"); Action acn = (pStep, pData) => { @@ -269,7 +266,6 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo }; step.Outputs.Add(new ActionParameter(acn)); - hasAddOutput = true; } else From d4d2d5645fc499c9f9be86469505efbba6c56cb4 Mon Sep 17 00:00:00 2001 From: Wangyang Ding Date: Thu, 27 May 2021 13:21:37 +0800 Subject: [PATCH 333/462] update unit test --- .../DataTypes/CounterBoardWithDynamicData.cs | 9 +++ test/WorkflowCore.TestAssets/Utils.cs | 4 ++ .../WorkflowCore.TestAssets.csproj | 4 ++ .../stored-dynamic-definition.yaml | 58 +++++++++++++++++++ .../DefinitionLoaderTests.cs | 10 ++++ 5 files changed, 85 insertions(+) create mode 100644 test/WorkflowCore.TestAssets/DataTypes/CounterBoardWithDynamicData.cs create mode 100644 test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml diff --git a/test/WorkflowCore.TestAssets/DataTypes/CounterBoardWithDynamicData.cs b/test/WorkflowCore.TestAssets/DataTypes/CounterBoardWithDynamicData.cs new file mode 100644 index 000000000..49875ba41 --- /dev/null +++ b/test/WorkflowCore.TestAssets/DataTypes/CounterBoardWithDynamicData.cs @@ -0,0 +1,9 @@ +using System; + +namespace WorkflowCore.TestAssets.DataTypes +{ + public class CounterBoardWithDynamicData: CounterBoard + { + public DynamicData DynamicDataInstance { get; set; } + } +} diff --git a/test/WorkflowCore.TestAssets/Utils.cs b/test/WorkflowCore.TestAssets/Utils.cs index 723cca992..b7a7919cd 100644 --- a/test/WorkflowCore.TestAssets/Utils.cs +++ b/test/WorkflowCore.TestAssets/Utils.cs @@ -30,6 +30,10 @@ public static string GetTestDefinitionDynamicJson() { return File.ReadAllText("stored-dynamic-definition.json"); } + public static string GetTestDefinitionDynamicYaml() + { + return File.ReadAllText("stored-dynamic-definition.yaml"); + } public static string GetTestDefinitionJsonMissingInputProperty() { diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index 12da1f57b..0ff0a1fc2 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -13,9 +13,13 @@ + + + Always + Always diff --git a/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml b/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml new file mode 100644 index 000000000..b14313366 --- /dev/null +++ b/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml @@ -0,0 +1,58 @@ +Id: Test +Version: 1 +DataType: WorkflowCore.TestAssets.DataTypes.CounterBoardWithDynamicData, WorkflowCore.TestAssets +Steps: +- Id: Step1 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + ErrorBehavior: Retry + Inputs: + Value: data.DynamicDataInstance["test"] + Outputs: + DynamicDataInstance["test"]: step.Value + NextStepId: Step2 +- Id: Step2 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter2 + Outputs: + Counter2: step.Value + NextStepId: Step3 +- Id: Step3 + StepType: WorkflowCore.Primitives.If, WorkflowCore + NextStepId: Step4 + Inputs: + Condition: object.Equals(data.Flag1, true) + Do: + - - Id: Step3.1.1 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter3 + Outputs: + Counter3: step.Value + NextStepId: Step3.1.2 + - Id: Step3.1.2 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter4 + Outputs: + Counter4: step.Value + - - Id: Step3.2.1 + StepType: WorkflowCore.Primitives.WaitFor, WorkflowCore + NextStepId: Step3.2.2 + CancelCondition: data.Flag2 + Inputs: + EventName: '"Event1"' + EventKey: '"Key1"' + EffectiveDate: DateTime.Now + - Id: Step3.2.2 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter5 + Outputs: + Counter5: step.Value +- Id: Step4 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter6 + Outputs: + Counter6: step.Value diff --git a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs index 6176b6ee6..01a743e8a 100644 --- a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs +++ b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs @@ -43,6 +43,16 @@ public void ParseDefinition() A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(MatchTestDefinition, ""))).MustHaveHappened(); } + [Fact(DisplayName = "Should parse definition")] + public void ParseDefinitionPropertyDynamic() + { + _subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionDynamicYaml(), Deserializers.Yaml); + + A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Id == "Test"))).MustHaveHappened(); + A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.Version == 1))).MustHaveHappened(); + A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(x => x.DataType == typeof(CounterBoardWithDynamicData)))).MustHaveHappened(); + A.CallTo(() => _registry.RegisterWorkflow(A.That.Matches(MatchTestDefinition, ""))).MustHaveHappened(); + } [Fact(DisplayName = "Should parse definition")] public void ParseDefinitionDynamic() From 5fd8b17c3c63d7a02167d840908443e8b30f862f Mon Sep 17 00:00:00 2001 From: Wangyang Ding Date: Thu, 27 May 2021 13:24:34 +0800 Subject: [PATCH 334/462] update unit test --- WorkflowCore.sln | 7 ------- 1 file changed, 7 deletions(-) diff --git a/WorkflowCore.sln b/WorkflowCore.sln index b80b2e757..de509ed05 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -152,8 +152,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample19", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.YmalDefinition", "test\WorkflowCore.Tests.YmalDefinition\WorkflowCore.Tests.YmalDefinition.csproj", "{099259BB-ABCD-4C7C-AFAE-56A2730B9309}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -376,10 +374,6 @@ Global {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Debug|Any CPU.Build.0 = Debug|Any CPU {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.Build.0 = Release|Any CPU - {099259BB-ABCD-4C7C-AFAE-56A2730B9309}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {099259BB-ABCD-4C7C-AFAE-56A2730B9309}.Debug|Any CPU.Build.0 = Debug|Any CPU - {099259BB-ABCD-4C7C-AFAE-56A2730B9309}.Release|Any CPU.ActiveCfg = Release|Any CPU - {099259BB-ABCD-4C7C-AFAE-56A2730B9309}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -442,7 +436,6 @@ Global {54DE20BA-EBA7-4BF0-9BD9-F03766849716} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {1223ED47-3E5E-4960-B70D-DFAF550F6666} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {AF205715-C8B7-42EF-BF14-AFC9E7F27242} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} - {099259BB-ABCD-4C7C-AFAE-56A2730B9309} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} From a84673ef0bf71f86395a909754116922df9bfcb9 Mon Sep 17 00:00:00 2001 From: Wangyang Ding Date: Thu, 27 May 2021 13:26:26 +0800 Subject: [PATCH 335/462] update unit test --- .../DictionaryData.yml | 10 ---- .../HelloWorld.yml | 24 ---------- .../MyDataClass.cs | 21 --------- .../Steps/HelloWorld.cs | 17 ------- .../WorkflowCore.Tests.YmalDefinition.csproj | 45 ------------------ .../YmalDefinitionTest.cs | 46 ------------------- 6 files changed, 163 deletions(-) delete mode 100644 test/WorkflowCore.Tests.YmalDefinition/DictionaryData.yml delete mode 100644 test/WorkflowCore.Tests.YmalDefinition/HelloWorld.yml delete mode 100644 test/WorkflowCore.Tests.YmalDefinition/MyDataClass.cs delete mode 100644 test/WorkflowCore.Tests.YmalDefinition/Steps/HelloWorld.cs delete mode 100644 test/WorkflowCore.Tests.YmalDefinition/WorkflowCore.Tests.YmalDefinition.csproj delete mode 100644 test/WorkflowCore.Tests.YmalDefinition/YmalDefinitionTest.cs diff --git a/test/WorkflowCore.Tests.YmalDefinition/DictionaryData.yml b/test/WorkflowCore.Tests.YmalDefinition/DictionaryData.yml deleted file mode 100644 index 9e8771448..000000000 --- a/test/WorkflowCore.Tests.YmalDefinition/DictionaryData.yml +++ /dev/null @@ -1,10 +0,0 @@ -Id: DictionaryData -Version: 1 -DataType: System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.Int32, System.Private.CoreLib]], System.Private.CoreLib -Steps: -- Id: HelloWorld1 - Name: HelloWorld1 - StepType: WorkflowCore.Tests.YmalDefinition.Steps.HelloWorld, WorkflowCore.Tests.YmalDefinition - Outputs: - Value1: 10 - Value2: 11 \ No newline at end of file diff --git a/test/WorkflowCore.Tests.YmalDefinition/HelloWorld.yml b/test/WorkflowCore.Tests.YmalDefinition/HelloWorld.yml deleted file mode 100644 index 1ad77d12e..000000000 --- a/test/WorkflowCore.Tests.YmalDefinition/HelloWorld.yml +++ /dev/null @@ -1,24 +0,0 @@ -Id: HelloWorld -Version: 1 -DataType: WorkflowCore.Tests.YmalDefinition.MyDataClass, WorkflowCore.Tests.YmalDefinition -Steps: -- Id: HelloWorld1 - Name: HelloWorld1 - StepType: WorkflowCore.Tests.YmalDefinition.Steps.HelloWorld, WorkflowCore.Tests.YmalDefinition - Outputs: - anotherData.Value3: 100121 - Value1: 10 - Value2: 11 - NextStepId: HelloWorld2 -- Id: HelloWorld2 - Name: HelloWorld2 - StepType: WorkflowCore.Tests.YmalDefinition.Steps.HelloWorld, WorkflowCore.Tests.YmalDefinition - Inputs: - Value: data.anotherData.Value3 - Outputs: - Dict["testKey1"]: 100 - Dict["testKey"]: 101 - NextStepId: HelloWorld3 -- Id: HelloWorld3 - Name: HelloWorld3 - StepType: WorkflowCore.Tests.YmalDefinition.Steps.HelloWorld, WorkflowCore.Tests.YmalDefinition diff --git a/test/WorkflowCore.Tests.YmalDefinition/MyDataClass.cs b/test/WorkflowCore.Tests.YmalDefinition/MyDataClass.cs deleted file mode 100644 index fc78e0c3b..000000000 --- a/test/WorkflowCore.Tests.YmalDefinition/MyDataClass.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace WorkflowCore.Tests.YmalDefinition -{ - public class MyDataClass - { - public int Value1 { get; set; } - - public int Value2 { get; set; } - - public Dictionary Dict { get; set; } - - public AnotherDataClass anotherData { get; set; } - } - public class AnotherDataClass - { - public int Value3 { get; set; } - } -} diff --git a/test/WorkflowCore.Tests.YmalDefinition/Steps/HelloWorld.cs b/test/WorkflowCore.Tests.YmalDefinition/Steps/HelloWorld.cs deleted file mode 100644 index 9b522e6f0..000000000 --- a/test/WorkflowCore.Tests.YmalDefinition/Steps/HelloWorld.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Linq; -using WorkflowCore.Interface; -using WorkflowCore.Models; - -namespace WorkflowCore.Tests.YmalDefinition.Steps -{ - public class HelloWorld : StepBody - { - public int Value { get; set; } = 10; - public override ExecutionResult Run(IStepExecutionContext context) - { - Console.WriteLine("Value is " + Value); - return ExecutionResult.Next(); - } - } -} diff --git a/test/WorkflowCore.Tests.YmalDefinition/WorkflowCore.Tests.YmalDefinition.csproj b/test/WorkflowCore.Tests.YmalDefinition/WorkflowCore.Tests.YmalDefinition.csproj deleted file mode 100644 index f948eb349..000000000 --- a/test/WorkflowCore.Tests.YmalDefinition/WorkflowCore.Tests.YmalDefinition.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - netcoreapp3.1 - - false - - - - - - - - - - - - - - - - - - - HelloWorld.yml - True - True - - - True - True - HelloWorld.yml - - - - - - Always - - - Always - - - - diff --git a/test/WorkflowCore.Tests.YmalDefinition/YmalDefinitionTest.cs b/test/WorkflowCore.Tests.YmalDefinition/YmalDefinitionTest.cs deleted file mode 100644 index 8b5533492..000000000 --- a/test/WorkflowCore.Tests.YmalDefinition/YmalDefinitionTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; -using WorkflowCore.Interface; -using WorkflowCore.Testing; -using Xunit; - -namespace WorkflowCore.Tests.YmalDefinition -{ - public class YmalDefinitionTest:YamlWorkflowTest - { - - [Fact] - public void LoadWorkflowDefinitions_NoException() - { - Setup(); - - MyDataClass myData = new MyDataClass() { - Value1 = 1, - Value2 = 2, - Dict = new Dictionary(), - anotherData = new AnotherDataClass() { Value3 = 3 } - }; - myData.Dict["testKey"] = 5; - - String workflowInstanceId=StartWorkflow(File.ReadAllText("HelloWorld.yml"), myData); - WaitForWorkflowToComplete(workflowInstanceId, TimeSpan.FromSeconds(30)); - MyDataClass resultData = GetData(workflowInstanceId); - Assert.Equal(10, resultData.Value1); - Assert.Equal(11, resultData.Value2); - Assert.Equal(100121, resultData.anotherData.Value3); - Assert.Equal(100, resultData.Dict["testKey1"]); - Assert.Equal(101, resultData.Dict["testKey"]); - - workflowInstanceId = StartWorkflow(File.ReadAllText("DictionaryData.yml"), new Dictionary()); - WaitForWorkflowToComplete(workflowInstanceId, TimeSpan.FromSeconds(30)); - Dictionary resultData2 = GetData>(workflowInstanceId); - Assert.Equal(10, resultData2["Value1"]); - Assert.Equal(11, resultData2["Value2"]); - } - } -} From cab5eae459beabb70ac0ea184b8552092de2dcd3 Mon Sep 17 00:00:00 2001 From: Wangyang Ding Date: Mon, 31 May 2021 10:38:50 +0800 Subject: [PATCH 336/462] update --- .../Services/DefinitionLoader.cs | 147 +++++++++++------- .../DataTypes/CounterBoardWithDynamicData.cs | 1 + .../stored-dynamic-definition.yaml | 2 +- 3 files changed, 89 insertions(+), 61 deletions(-) diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index ca50cffd8..90b830990 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -217,90 +217,117 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { stepParameter }, typeof(object), output.Value); var dataParameter = Expression.Parameter(dataType, "data"); - Expression targetProperty = dataParameter; - // Check if our datatype has a matching property - PropertyInfo propertyInfo = null; - String[] paths = output.Key.Split('.'); - - bool hasAddOutput = false; - - foreach (String propertyName in paths) + if(output.Key.Contains(".") || output.Key.Contains("[")) { - if (hasAddOutput) - { - throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); - } + AttachNestedOutput(output, step, source, sourceExpr, dataParameter); + }else + { + AttachDirectlyOutput(output, step, dataType, sourceExpr, dataParameter); + } + } + } - if (targetProperty == null) - { - break; - } + private void AttachDirectlyOutput(KeyValuePair output, WorkflowStep step, Type dataType, LambdaExpression sourceExpr, ParameterExpression dataParameter) + { + Expression targetProperty; - if (propertyName.Contains("[")) - { - String[] items = propertyName.Split('['); + // Check if our datatype has a matching property + var propertyInfo = dataType.GetProperty(output.Key); + if (propertyInfo != null) + { + targetProperty = Expression.Property(dataParameter, propertyInfo); + var targetExpr = Expression.Lambda(targetProperty, dataParameter); + step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr)); + } + else + { + // If we did not find a matching property try to find a Indexer with string parameter + propertyInfo = dataType.GetProperty("Item"); + targetProperty = Expression.Property(dataParameter, propertyInfo, Expression.Constant(output.Key)); - if (items.Length != 2) - { - throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); - } + Action acn = (pStep, pData) => + { + object resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); ; + propertyInfo.SetValue(pData, resolvedValue, new object[] { output.Key }); + }; - items[1] = items[1].Trim().TrimEnd(']').Trim().Trim('"'); + step.Outputs.Add(new ActionParameter(acn)); + } - MemberExpression memberExpression = Expression.Property(targetProperty, items[0]); + } - if (memberExpression == null) - { - throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); - } - propertyInfo = ((PropertyInfo)memberExpression.Member).PropertyType.GetProperty("Item"); + private void AttachNestedOutput( KeyValuePair output, WorkflowStep step, StepSourceV1 source, LambdaExpression sourceExpr, ParameterExpression dataParameter) + { + PropertyInfo propertyInfo = null; + String[] paths = output.Key.Split('.'); + + Expression targetProperty = dataParameter; - Action acn = (pStep, pData) => - { - var targetExpr = Expression.Lambda(memberExpression, dataParameter); - object data = targetExpr.Compile().DynamicInvoke(pData); - object resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); ; - propertyInfo.SetValue(data, resolvedValue, new object[] { items[1] }); - }; - - step.Outputs.Add(new ActionParameter(acn)); - hasAddOutput = true; - } - else - { - try - { - targetProperty = Expression.Property(targetProperty, propertyName); - }catch - { - targetProperty = null; - break; - } - } + bool hasAddOutput = false; + + foreach (String propertyName in paths) + { + if (hasAddOutput) + { + throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); } - if (targetProperty != null && ! hasAddOutput) + if (targetProperty == null) { - var targetExpr = Expression.Lambda(targetProperty, dataParameter); - step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr)); + break; } - else if (! hasAddOutput) + + if (propertyName.Contains("[")) { - // If we did not find a matching property try to find a Indexer with string parameter - propertyInfo = dataType.GetProperty("Item"); - targetProperty = Expression.Property(dataParameter, propertyInfo, Expression.Constant(output.Key)); + String[] items = propertyName.Split('['); + + if (items.Length != 2) + { + throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); + } + + items[1] = items[1].Trim().TrimEnd(']').Trim().Trim('"'); + + MemberExpression memberExpression = Expression.Property(targetProperty, items[0]); + + if (memberExpression == null) + { + throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); + } + propertyInfo = ((PropertyInfo)memberExpression.Member).PropertyType.GetProperty("Item"); Action acn = (pStep, pData) => { + var targetExpr = Expression.Lambda(memberExpression, dataParameter); + object data = targetExpr.Compile().DynamicInvoke(pData); object resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); ; - propertyInfo.SetValue(pData, resolvedValue, new object[] { output.Key }); + propertyInfo.SetValue(data, resolvedValue, new object[] { items[1] }); }; step.Outputs.Add(new ActionParameter(acn)); + hasAddOutput = true; + } + else + { + try + { + targetProperty = Expression.Property(targetProperty, propertyName); + } + catch + { + targetProperty = null; + break; + } } } + + if (targetProperty != null && !hasAddOutput) + { + var targetExpr = Expression.Lambda(targetProperty, dataParameter); + step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr)); + } } private void AttachOutcomes(StepSourceV1 source, Type dataType, WorkflowStep step) diff --git a/test/WorkflowCore.TestAssets/DataTypes/CounterBoardWithDynamicData.cs b/test/WorkflowCore.TestAssets/DataTypes/CounterBoardWithDynamicData.cs index 49875ba41..3a708724d 100644 --- a/test/WorkflowCore.TestAssets/DataTypes/CounterBoardWithDynamicData.cs +++ b/test/WorkflowCore.TestAssets/DataTypes/CounterBoardWithDynamicData.cs @@ -5,5 +5,6 @@ namespace WorkflowCore.TestAssets.DataTypes public class CounterBoardWithDynamicData: CounterBoard { public DynamicData DynamicDataInstance { get; set; } + public CounterBoard CounterBoardInstance { get; set; } } } diff --git a/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml b/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml index b14313366..6c38cd67f 100644 --- a/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml +++ b/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml @@ -15,7 +15,7 @@ Steps: Inputs: Value: data.Counter2 Outputs: - Counter2: step.Value + CounterBoardInstance.Counter1: step.Value NextStepId: Step3 - Id: Step3 StepType: WorkflowCore.Primitives.If, WorkflowCore From 2d1962c82313e32646607ef7827ee62df414623f Mon Sep 17 00:00:00 2001 From: lyzerk Date: Tue, 1 Jun 2021 12:31:10 +0300 Subject: [PATCH 337/462] ActivityName as expression. It solves #542 issue. --- WorkflowCore.sln | 7 ++ .../Interface/IWorkflowModifier.cs | 10 +++ .../Services/FluentBuilders/StepBuilder.cs | 19 +++++ .../FluentBuilders/WorkflowBuilder.cs | 4 + .../WorkflowCore.Sample20/ActivityWorkflow.cs | 29 +++++++ src/samples/WorkflowCore.Sample20/Program.cs | 53 +++++++++++++ src/samples/WorkflowCore.Sample20/README.md | 34 +++++++++ .../Steps/CustomMessage.cs | 19 +++++ .../Steps/GoodbyeWorld.cs | 16 ++++ .../WorkflowCore.Sample20/Steps/HelloWorld.cs | 16 ++++ .../WorkflowCore.Sample20.csproj | 20 +++++ .../Scenarios/ActivityScenario2.cs | 75 +++++++++++++++++++ 12 files changed, 302 insertions(+) create mode 100644 src/samples/WorkflowCore.Sample20/ActivityWorkflow.cs create mode 100644 src/samples/WorkflowCore.Sample20/Program.cs create mode 100644 src/samples/WorkflowCore.Sample20/README.md create mode 100644 src/samples/WorkflowCore.Sample20/Steps/CustomMessage.cs create mode 100644 src/samples/WorkflowCore.Sample20/Steps/GoodbyeWorld.cs create mode 100644 src/samples/WorkflowCore.Sample20/Steps/HelloWorld.cs create mode 100644 src/samples/WorkflowCore.Sample20/WorkflowCore.Sample20.csproj create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario2.cs diff --git a/WorkflowCore.sln b/WorkflowCore.sln index be18fd721..8bd5d6ca1 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -152,6 +152,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample19", "sr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample20", "src\samples\WorkflowCore.Sample20\WorkflowCore.Sample20.csproj", "{68D1B955-1049-477B-A894-13FCB96C45DE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -374,6 +376,10 @@ Global {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Debug|Any CPU.Build.0 = Debug|Any CPU {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.Build.0 = Release|Any CPU + {68D1B955-1049-477B-A894-13FCB96C45DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68D1B955-1049-477B-A894-13FCB96C45DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68D1B955-1049-477B-A894-13FCB96C45DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68D1B955-1049-477B-A894-13FCB96C45DE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -436,6 +442,7 @@ Global {54DE20BA-EBA7-4BF0-9BD9-F03766849716} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {1223ED47-3E5E-4960-B70D-DFAF550F6666} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {AF205715-C8B7-42EF-BF14-AFC9E7F27242} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} + {68D1B955-1049-477B-A894-13FCB96C45DE} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/WorkflowCore/Interface/IWorkflowModifier.cs b/src/WorkflowCore/Interface/IWorkflowModifier.cs index d8af82616..1ecd093d0 100644 --- a/src/WorkflowCore/Interface/IWorkflowModifier.cs +++ b/src/WorkflowCore/Interface/IWorkflowModifier.cs @@ -152,5 +152,15 @@ IContainerStepBuilder Recur(Expression Activity(string activityName, Expression> parameters = null, Expression> effectiveDate = null, Expression> cancelCondition = null); + /// + /// Wait here until an external activity is complete + /// + /// The name used to identify the activity to wait for + /// The data to pass the external activity worker + /// Listen for events as of this effective date + /// A conditon that when true will cancel this WaitFor + /// + IStepBuilder Activity(Expression> activityName, Expression> parameters = null, + Expression> effectiveDate = null, Expression> cancelCondition = null); } } \ No newline at end of file diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index f5d25730a..9cfeae852 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -522,5 +522,24 @@ public IStepBuilder Activity(string activityName, Expression Activity(Expression> activityName, Expression> parameters = null, Expression> effectiveDate = null, Expression> cancelCondition = null) + { + var newStep = new WorkflowStep(); + newStep.CancelCondition = cancelCondition; + + WorkflowBuilder.AddStep(newStep); + var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); + stepBuilder.Input((step) => step.ActivityName, activityName); + + if (parameters != null) + stepBuilder.Input((step) => step.Parameters, parameters); + + if (effectiveDate != null) + stepBuilder.Input((step) => step.EffectiveDate, effectiveDate); + + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); + return stepBuilder; + } } } diff --git a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs index 98788fa0c..f9d55f77e 100644 --- a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs @@ -271,6 +271,10 @@ public IStepBuilder Activity(string activityName, Expression Activity(Expression> activityName, Expression> parameters = null, Expression> effectiveDate = null, Expression> cancelCondition = null) + { + return Start().Activity(activityName, parameters, effectiveDate, cancelCondition); + } private IStepBuilder Start() { diff --git a/src/samples/WorkflowCore.Sample20/ActivityWorkflow.cs b/src/samples/WorkflowCore.Sample20/ActivityWorkflow.cs new file mode 100644 index 000000000..03208dcfd --- /dev/null +++ b/src/samples/WorkflowCore.Sample20/ActivityWorkflow.cs @@ -0,0 +1,29 @@ +using System; +using WorkflowCore.Interface; +using WorkflowCore.Sample20.Steps; + +namespace WorkflowCore.Sample20 +{ + class ActivityWorkflow : IWorkflow + { + public string Id => "activity-sample"; + public int Version => 1; + + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith() + .Activity((data, context) => "get-approval-" + context.Workflow.Id, (data) => data.Request) + .Output(data => data.ApprovedBy, step => step.Result) + .Then() + .Input(step => step.Message, data => "Approved by " + data.ApprovedBy) + .Then(); + } + } + + class MyData + { + public string Request { get; set; } + public string ApprovedBy { get; set; } + } +} diff --git a/src/samples/WorkflowCore.Sample20/Program.cs b/src/samples/WorkflowCore.Sample20/Program.cs new file mode 100644 index 000000000..69009fe98 --- /dev/null +++ b/src/samples/WorkflowCore.Sample20/Program.cs @@ -0,0 +1,53 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using WorkflowCore.Interface; + +namespace WorkflowCore.Sample20 +{ + class Program + { + static void Main(string[] args) + { + var serviceProvider = ConfigureServices(); + + //start the workflow host + var host = serviceProvider.GetService(); + host.RegisterWorkflow(); + host.Start(); + + Console.WriteLine("Starting workflow..."); + + var workflowId = host.StartWorkflow("activity-sample", new MyData { Request = "Spend $1,000,000" }).Result; + + var approval = host.GetPendingActivity("get-approval-" + workflowId, "worker1", TimeSpan.FromMinutes(1)).Result; + + if (approval != null) + { + Console.WriteLine("Approval required for " + approval.Parameters); + host.SubmitActivitySuccess(approval.Token, "John Smith"); + } + + Console.ReadLine(); + host.Stop(); + } + + private static IServiceProvider ConfigureServices() + { + //setup dependency injection + IServiceCollection services = new ServiceCollection(); + //services.AddWorkflow(); + services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); + //services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); + //services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;", true, true)); + services.AddLogging(cfg => + { + cfg.AddConsole(); + cfg.AddDebug(); + }); + + var serviceProvider = services.BuildServiceProvider(); + return serviceProvider; + } + } +} diff --git a/src/samples/WorkflowCore.Sample20/README.md b/src/samples/WorkflowCore.Sample20/README.md new file mode 100644 index 000000000..46f5d1e22 --- /dev/null +++ b/src/samples/WorkflowCore.Sample20/README.md @@ -0,0 +1,34 @@ +# Activity sample + +Illustrates how to have your workflow wait for an external activity that is fulfilled by a worker that you implement. + +This workflow will wait for the `get-approval-{workflowId}` activity and pass the request string to it as an input. + +The main reason behind of this example is Activities are global listeners. Therefore, in some cases, you want to be sure about its uniqueness. + +Also, you can take look the issue https://github.com/danielgerlag/workflow-core/issues/542 + +```c# +builder + .StartWith() + .Activity((data, context) => context.Workflow.Id, (data) => data.Request) + .Output(data => data.ApprovedBy, step => step.Result) + .Then() + .Input(step => step.Message, data => "Approved by " + data.ApprovedBy) + .Then(); +``` + +Then we implement an activity worker to pull pending activities of type `get-approval`, where we can inspect the input and submit a response back to the waiting workflow. + +```c# +var workflowId = host.StartWorkflow("activity-sample", new MyData { Request = "Spend $1,000,000" }).Result; + +var approval = host.GetPendingActivity("get-approval-" + workflowId, "worker1", TimeSpan.FromMinutes(1)).Result; + +if (approval != null) +{ + Console.WriteLine("Approval required for " + approval.Parameters); + host.SubmitActivitySuccess(approval.Token, "John Smith"); +} +``` + diff --git a/src/samples/WorkflowCore.Sample20/Steps/CustomMessage.cs b/src/samples/WorkflowCore.Sample20/Steps/CustomMessage.cs new file mode 100644 index 000000000..cad8eff61 --- /dev/null +++ b/src/samples/WorkflowCore.Sample20/Steps/CustomMessage.cs @@ -0,0 +1,19 @@ +using System; +using System.Linq; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample20.Steps +{ + public class CustomMessage : StepBody + { + + public string Message { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine(Message); + return ExecutionResult.Next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample20/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.Sample20/Steps/GoodbyeWorld.cs new file mode 100644 index 000000000..0908966d9 --- /dev/null +++ b/src/samples/WorkflowCore.Sample20/Steps/GoodbyeWorld.cs @@ -0,0 +1,16 @@ +using System; +using System.Linq; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample20.Steps +{ + public class GoodbyeWorld : StepBody + { + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Goodbye world"); + return ExecutionResult.Next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample20/Steps/HelloWorld.cs b/src/samples/WorkflowCore.Sample20/Steps/HelloWorld.cs new file mode 100644 index 000000000..5623bf684 --- /dev/null +++ b/src/samples/WorkflowCore.Sample20/Steps/HelloWorld.cs @@ -0,0 +1,16 @@ +using System; +using System.Linq; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Sample20.Steps +{ + public class HelloWorld : StepBody + { + public override ExecutionResult Run(IStepExecutionContext context) + { + Console.WriteLine("Hello world"); + return ExecutionResult.Next(); + } + } +} diff --git a/src/samples/WorkflowCore.Sample20/WorkflowCore.Sample20.csproj b/src/samples/WorkflowCore.Sample20/WorkflowCore.Sample20.csproj new file mode 100644 index 000000000..660b47511 --- /dev/null +++ b/src/samples/WorkflowCore.Sample20/WorkflowCore.Sample20.csproj @@ -0,0 +1,20 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + + + + + + + + diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario2.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario2.cs new file mode 100644 index 000000000..356f77402 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario2.cs @@ -0,0 +1,75 @@ +using System; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using System.Linq; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class ActivityScenario2 : WorkflowTest + { + public class MyDataClass + { + public object ActivityInput { get; set; } + public object ActivityOutput { get; set; } + } + + public class ActivityInput + { + public string Value1 { get; set; } + public int Value2 { get; set; } + } + + public class ActivityOutput + { + public string Value1 { get; set; } + public int Value2 { get; set; } + } + + public class ActivityWorkflow : IWorkflow + { + public string Id => "ActivityWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .Activity((_, context) => "act-1-" + context.Workflow.Id, data => data.ActivityInput) + .Output(data => data.ActivityOutput, step => step.Result); + } + } + + public ActivityScenario2() + { + Setup(); + } + + [Fact] + public void Scenario() + { + // compound key + var workflowId = StartWorkflow(new MyDataClass { ActivityInput = new ActivityInput { Value1 = "a", Value2 = 1 } }); + var activity = Host.GetPendingActivity("act-1-" + workflowId, "worker1", TimeSpan.FromSeconds(30)).Result; + + if (activity != null) + { + var actInput = (ActivityInput)activity.Parameters; + Host.SubmitActivitySuccess(activity.Token, new ActivityOutput + { + Value1 = actInput.Value1 + "1", + Value2 = actInput.Value2 + 1 + }); + } + + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + GetData(workflowId).ActivityOutput.Should().BeOfType(); + var outData = (GetData(workflowId).ActivityOutput as ActivityOutput); + outData.Value1.Should().Be("a1"); + outData.Value2.Should().Be(2); + } + } +} From 42fdc797b387c3bac3ddd3e9d96568ba4fd5e694 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Thu, 3 Jun 2021 11:15:38 +0300 Subject: [PATCH 338/462] Allow choose name of container for Cosmos DB --- .../Interface/ICosmosDbProvisioner.cs | 5 +-- .../ServiceCollectionExtensions.cs | 15 ++++++-- .../Services/CosmosDbPersistenceProvider.cs | 30 ++++++++-------- .../Services/CosmosDbProvisioner.cs | 22 ++++++------ .../Services/CosmosDbStorageOptions.cs | 35 +++++++++++++++++++ .../WorkflowCore.Providers.Azure.csproj | 4 +-- 6 files changed, 79 insertions(+), 32 deletions(-) create mode 100644 src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbStorageOptions.cs diff --git a/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbProvisioner.cs b/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbProvisioner.cs index 67a738971..d24cca4d8 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Interface/ICosmosDbProvisioner.cs @@ -1,9 +1,10 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; namespace WorkflowCore.Providers.Azure.Interface { public interface ICosmosDbProvisioner { - Task Provision(string dbId); + Task Provision(string dbId, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs index 96fff8f1d..92718cb95 100644 --- a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs @@ -26,11 +26,20 @@ public static WorkflowOptions UseAzureServiceBusEventHub( return options; } - public static WorkflowOptions UseCosmosDbPersistence(this WorkflowOptions options, string connectionString, string databaseId) + public static WorkflowOptions UseCosmosDbPersistence( + this WorkflowOptions options, + string connectionString, + string databaseId, + CosmosDbStorageOptions cosmosDbStorageOptions = null) { + if (cosmosDbStorageOptions == null) + { + cosmosDbStorageOptions = new CosmosDbStorageOptions(); + } + options.Services.AddSingleton(sp => new CosmosClientFactory(connectionString)); - options.Services.AddTransient(sp => new CosmosDbProvisioner(sp.GetService(), sp.GetService())); - options.UsePersistence(sp => new CosmosDbPersistenceProvider(sp.GetService(), databaseId, sp.GetService())); + options.Services.AddTransient(sp => new CosmosDbProvisioner(sp.GetService(), cosmosDbStorageOptions)); + options.UsePersistence(sp => new CosmosDbPersistenceProvider(sp.GetService(), databaseId, sp.GetService(), cosmosDbStorageOptions)); return options; } } diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs index aa5d62ce0..36e51b86c 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs @@ -14,25 +14,25 @@ namespace WorkflowCore.Providers.Azure.Services { public class CosmosDbPersistenceProvider : IPersistenceProvider { - public const string WorkflowContainerName = "workflows"; - public const string EventContainerName = "events"; - public const string SubscriptionContainerName = "subscriptions"; - - private ICosmosDbProvisioner _provisioner; - private string _dbId; - private ICosmosClientFactory _clientFactory; - private Lazy _workflowContainer; - private Lazy _eventContainer; - private Lazy _subscriptionContainer; - - public CosmosDbPersistenceProvider(ICosmosClientFactory clientFactory, string dbId, ICosmosDbProvisioner provisioner) + private readonly ICosmosDbProvisioner _provisioner; + private readonly string _dbId; + private readonly ICosmosClientFactory _clientFactory; + private readonly Lazy _workflowContainer; + private readonly Lazy _eventContainer; + private readonly Lazy _subscriptionContainer; + + public CosmosDbPersistenceProvider( + ICosmosClientFactory clientFactory, + string dbId, + ICosmosDbProvisioner provisioner, + CosmosDbStorageOptions cosmosDbStorageOptions) { _provisioner = provisioner; _dbId = dbId; _clientFactory = clientFactory; - _workflowContainer = new Lazy(() => _clientFactory.GetCosmosClient().GetDatabase(_dbId).GetContainer(WorkflowContainerName)); - _eventContainer = new Lazy(() => _clientFactory.GetCosmosClient().GetDatabase(_dbId).GetContainer(EventContainerName)); - _subscriptionContainer = new Lazy(() => _clientFactory.GetCosmosClient().GetDatabase(_dbId).GetContainer(SubscriptionContainerName)); + _workflowContainer = new Lazy(() => _clientFactory.GetCosmosClient().GetDatabase(_dbId).GetContainer(cosmosDbStorageOptions.WorkflowContainerName)); + _eventContainer = new Lazy(() => _clientFactory.GetCosmosClient().GetDatabase(_dbId).GetContainer(cosmosDbStorageOptions.EventContainerName)); + _subscriptionContainer = new Lazy(() => _clientFactory.GetCosmosClient().GetDatabase(_dbId).GetContainer(cosmosDbStorageOptions.SubscriptionContainerName)); } public async Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken cancellationToken = default) diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs index 08e51c2f4..54aa21435 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbProvisioner.cs @@ -1,35 +1,37 @@ -using System; +using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos; -using Microsoft.Extensions.Logging; using WorkflowCore.Providers.Azure.Interface; namespace WorkflowCore.Providers.Azure.Services { public class CosmosDbProvisioner : ICosmosDbProvisioner { + private readonly ICosmosClientFactory _clientFactory; + private readonly CosmosDbStorageOptions _cosmosDbStorageOptions; - private ICosmosClientFactory _clientFactory; - - public CosmosDbProvisioner(ICosmosClientFactory clientFactory, ILoggerFactory loggerFactory) + public CosmosDbProvisioner( + ICosmosClientFactory clientFactory, + CosmosDbStorageOptions cosmosDbStorageOptions) { _clientFactory = clientFactory; + _cosmosDbStorageOptions = cosmosDbStorageOptions; } - public async Task Provision(string dbId) + public async Task Provision(string dbId, CancellationToken cancellationToken = default) { - var dbResp = await _clientFactory.GetCosmosClient().CreateDatabaseIfNotExistsAsync(dbId); + var dbResp = await _clientFactory.GetCosmosClient().CreateDatabaseIfNotExistsAsync(dbId, cancellationToken: cancellationToken); var wfIndexPolicy = new IndexingPolicy(); wfIndexPolicy.IncludedPaths.Add(new IncludedPath { Path = @"/*" }); wfIndexPolicy.ExcludedPaths.Add(new ExcludedPath { Path = @"/ExecutionPointers/?" }); Task.WaitAll( - dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(CosmosDbPersistenceProvider.WorkflowContainerName, @"/id") + dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(_cosmosDbStorageOptions.WorkflowContainerName, @"/id") { IndexingPolicy = wfIndexPolicy }), - dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(CosmosDbPersistenceProvider.EventContainerName, @"/id")), - dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(CosmosDbPersistenceProvider.SubscriptionContainerName, @"/id")) + dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(_cosmosDbStorageOptions.EventContainerName, @"/id")), + dbResp.Database.CreateContainerIfNotExistsAsync(new ContainerProperties(_cosmosDbStorageOptions.SubscriptionContainerName, @"/id")) ); } } diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbStorageOptions.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbStorageOptions.cs new file mode 100644 index 000000000..fc89c89b0 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbStorageOptions.cs @@ -0,0 +1,35 @@ +namespace WorkflowCore.Providers.Azure.Services +{ + public sealed class CosmosDbStorageOptions + { + /// + /// The default name of workflow container. + /// + public const string DefaultWorkflowContainerName = "workflows"; + + /// + /// The name of Workflow container in Cosmos DB. + /// + public string WorkflowContainerName { get; set; } = DefaultWorkflowContainerName; + + /// + /// The default name of event container. + /// + public const string DefaultEventContainerName = "events"; + + /// + /// The name of Event container in Cosmos DB. + /// + public string EventContainerName { get; set; } = DefaultEventContainerName; + + /// + /// The default name of subscription container. + /// + public const string DefaultSubscriptionContainerName = "subscriptions"; + + /// + /// The name of Subscription container in Cosmos DB. + /// + public string SubscriptionContainerName { get; set; } = DefaultSubscriptionContainerName; + } +} diff --git a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj index dc7e5609a..cedd72024 100644 --- a/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj +++ b/src/providers/WorkflowCore.Providers.Azure/WorkflowCore.Providers.Azure.csproj @@ -16,9 +16,9 @@ - + - + From 2c594a00c1d1c8053783f36cbb6103dd0cd878b7 Mon Sep 17 00:00:00 2001 From: "Simon A. Eugster" Date: Wed, 9 Jun 2021 09:44:09 +0200 Subject: [PATCH 339/462] Fix lifecycle events when restarting Workflow Core --- WorkflowCore.sln | 2 +- .../Interface/IDistributedLockProvider.cs | 6 +++ .../Services/LifeCycleEventPublisher.cs | 7 ++- src/WorkflowCore/Services/WorkflowHost.cs | 10 +++- .../Services/LifeCycleEventPublisherTests.cs | 54 +++++++++++++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 test/WorkflowCore.UnitTests/Services/LifeCycleEventPublisherTests.cs diff --git a/WorkflowCore.sln b/WorkflowCore.sln index be18fd721..58387d6ed 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -7,7 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF47161E-E39 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F6AC9AEB-24EF-475A-B190-AA4D9E01270A}" ProjectSection(SolutionItems) = preProject - readme.md = readme.md + README.md = README.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{5080DB09-CBE8-4C45-9957-C3BB7651755E}" diff --git a/src/WorkflowCore/Interface/IDistributedLockProvider.cs b/src/WorkflowCore/Interface/IDistributedLockProvider.cs index 3c2018e42..498fafe5b 100644 --- a/src/WorkflowCore/Interface/IDistributedLockProvider.cs +++ b/src/WorkflowCore/Interface/IDistributedLockProvider.cs @@ -9,6 +9,12 @@ namespace WorkflowCore.Interface /// public interface IDistributedLockProvider { + /// + /// Acquire a lock on the specified resource. + /// + /// Resource ID to lock. + /// + /// `true`, if the lock was acquired. Task AcquireLock(string Id, CancellationToken cancellationToken); Task ReleaseLock(string Id); diff --git a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs index ffebb332e..1dce43e4c 100644 --- a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs +++ b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs @@ -11,7 +11,7 @@ public class LifeCycleEventPublisher : ILifeCycleEventPublisher, IDisposable { private readonly ILifeCycleEventHub _eventHub; private readonly ILogger _logger; - private readonly BlockingCollection _outbox; + private BlockingCollection _outbox; private Task _dispatchTask; public LifeCycleEventPublisher(ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory) @@ -36,6 +36,11 @@ public void Start() throw new InvalidOperationException(); } + if (_outbox.IsAddingCompleted) + { + _outbox = new BlockingCollection(); + } + _dispatchTask = new Task(Execute); _dispatchTask.Start(); } diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index 2a7fcc330..ca5e04b2f 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -47,7 +47,6 @@ public WorkflowHost(IPersistenceProvider persistenceStore, IQueueProvider queueP _searchIndex = searchIndex; _activityController = activityController; _lifeCycleEventHub = lifeCycleEventHub; - _lifeCycleEventHub.Subscribe(HandleLifeCycleEvent); } public Task StartWorkflow(string workflowId, object data = null, string reference=null) @@ -91,6 +90,10 @@ public async Task StartAsync(CancellationToken cancellationToken) await _lifeCycleEventHub.Start(); await _searchIndex.Start(); + // Event subscriptions are removed when stopping the event hub. + // Add them when starting. + AddEventSubscriptions(); + Logger.LogInformation("Starting background tasks"); foreach (var task in _backgroundTasks) @@ -181,5 +184,10 @@ public Task SubmitActivityFailure(string token, object result) { return _activityController.SubmitActivityFailure(token, result); } + + private void AddEventSubscriptions() + { + _lifeCycleEventHub.Subscribe(HandleLifeCycleEvent); + } } } diff --git a/test/WorkflowCore.UnitTests/Services/LifeCycleEventPublisherTests.cs b/test/WorkflowCore.UnitTests/Services/LifeCycleEventPublisherTests.cs new file mode 100644 index 000000000..bfee3ad07 --- /dev/null +++ b/test/WorkflowCore.UnitTests/Services/LifeCycleEventPublisherTests.cs @@ -0,0 +1,54 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Moq; +using WorkflowCore.Interface; +using WorkflowCore.Models.LifeCycleEvents; +using WorkflowCore.Services; +using Xunit; + +namespace WorkflowCore.UnitTests.Services +{ + public class LifeCycleEventPublisherTests + { + [Fact(DisplayName = "Notifications should be published when the publisher is running")] + public async Task PublishNotification_WhenStarted_PublishesNotification() + { + // Arrange + var wasCalled = new TaskCompletionSource(); + var eventHubMock = new Mock(); + eventHubMock + .Setup(hub => hub.PublishNotification(It.IsAny())) + .Callback(() => wasCalled.SetResult(true)); + LifeCycleEventPublisher publisher = new LifeCycleEventPublisher(eventHubMock.Object, new LoggerFactory()); + + // Act + publisher.Start(); + publisher.PublishNotification(new StepCompleted()); + + // Assert + await wasCalled.Task; + } + + [Fact(DisplayName = "Notifications should be published when the publisher is running")] + public async Task PublishNotification_WhenRestarted_PublishesNotification() + { + // Arrange + var wasCalled = new TaskCompletionSource(); + var eventHubMock = new Mock(); + eventHubMock + .Setup(hub => hub.PublishNotification(It.IsAny())) + .Callback(() => wasCalled.SetResult(true)); + LifeCycleEventPublisher publisher = new LifeCycleEventPublisher(eventHubMock.Object, new LoggerFactory()); + + // Act + publisher.Start(); + publisher.Stop(); + publisher.Start(); + publisher.PublishNotification(new StepCompleted()); + + // Assert + await wasCalled.Task; + } + } +} \ No newline at end of file From 66b6e45673b6967d4ea990e728f303da98b4c0b5 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Wed, 23 Jun 2021 18:53:06 +0300 Subject: [PATCH 340/462] Wait until lock will be postponed --- .../Services/BackgroundTasks/WorkflowConsumer.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 7c0637482..2faba7b87 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -89,8 +89,19 @@ private async Task SubscribeEvent(EventSubscription subscription, IPersistencePr if (subscription.EventName != Event.EventTypeActivity) { var events = await persistenceStore.GetEvents(subscription.EventName, subscription.EventKey, subscription.SubscribeAsOf, cancellationToken); + foreach (var evt in events) { + var locked = await _lockProvider.AcquireLock($"evt:{evt}", cancellationToken); + int attempt = 0; + while (locked && attempt < 10) + { + locked = await _lockProvider.AcquireLock($"evt:{evt}", cancellationToken); + await Task.Delay(Options.IdleTime); + + attempt++; + } + await persistenceStore.MarkEventUnprocessed(evt, cancellationToken); await QueueProvider.QueueWork(evt, QueueType.Event); } From 2746d0529b446c0d64d7e0d032508ea4b1ff4150 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Wed, 23 Jun 2021 19:49:20 +0300 Subject: [PATCH 341/462] Use Lock on event subscription --- .../BackgroundTasks/WorkflowConsumer.cs | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 2faba7b87..4253678cc 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -92,18 +92,30 @@ private async Task SubscribeEvent(EventSubscription subscription, IPersistencePr foreach (var evt in events) { - var locked = await _lockProvider.AcquireLock($"evt:{evt}", cancellationToken); - int attempt = 0; - while (locked && attempt < 10) + var eventKey = $"evt:{evt}"; + bool acquiredLock = false; + try { - locked = await _lockProvider.AcquireLock($"evt:{evt}", cancellationToken); - await Task.Delay(Options.IdleTime); + acquiredLock = await _lockProvider.AcquireLock(eventKey, cancellationToken); + int attempt = 0; + while (!acquiredLock && attempt < 10) + { + acquiredLock = await _lockProvider.AcquireLock(eventKey, cancellationToken); + await Task.Delay(Options.IdleTime); - attempt++; - } + attempt++; + } - await persistenceStore.MarkEventUnprocessed(evt, cancellationToken); - await QueueProvider.QueueWork(evt, QueueType.Event); + await persistenceStore.MarkEventUnprocessed(evt, cancellationToken); + await QueueProvider.QueueWork(evt, QueueType.Event); + } + finally + { + if (acquiredLock) + { + await _lockProvider.ReleaseLock(eventKey); + } + } } } } From 0ac318dd37f814bd6c442409fe44dbe41ddf06db Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 23 Jun 2021 22:16:47 -0700 Subject: [PATCH 342/462] bump version --- test/WorkflowCore.Testing/WorkflowCore.Testing.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index fd6508887..4449e3fa6 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 2.3.0 - 2.3.0.0 - 2.3.0.0 + 3.5.0 + 3.5.0.0 + 3.5.0.0 Facilitates testing of workflows built on Workflow-Core From 91ee43d3fd7cf1d2dbd4bfc910d8f71d75f811c2 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Tue, 29 Jun 2021 17:36:41 +0300 Subject: [PATCH 343/462] Fix greylist foerever stack issue --- .../Services/BackgroundTasks/EventConsumer.cs | 8 +++++++- .../Services/BackgroundTasks/RunnablePoller.cs | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index f533a32ff..9635df3ad 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -74,7 +74,13 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance complete = complete && await SeedSubscription(evt, sub, toQueue, cancellationToken); if (complete) + { await _eventRepository.MarkEventProcessed(itemId, cancellationToken); + } + else + { + _greylist.Remove($"evt:{evt.Id}"); + } foreach (var eventId in toQueue) await QueueProvider.QueueWork(eventId, QueueType.Event); @@ -87,7 +93,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance } private async Task SeedSubscription(Event evt, EventSubscription sub, HashSet toQueue, CancellationToken cancellationToken) - { + { foreach (var eventId in await _eventRepository.GetEvents(sub.EventName, sub.EventKey, sub.SubscribeAsOf, cancellationToken)) { if (eventId == evt.Id) diff --git a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs index 6a6bdf79c..9d5edc575 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs @@ -93,7 +93,6 @@ private async void PollRunnables(object target) if (_greylist.Contains($"evt:{item}")) { _logger.LogDebug($"Got greylisted event {item}"); - _greylist.Add($"evt:{item}"); continue; } _logger.LogDebug($"Got unprocessed event {item}"); From e67e98c0f69bb6039e6c515c0405f15d0e6acd03 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Tue, 29 Jun 2021 17:41:00 +0300 Subject: [PATCH 344/462] Greylist fixes in workflow consumer --- .../Services/BackgroundTasks/WorkflowConsumer.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 4253678cc..23227d95d 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -84,7 +84,7 @@ private async Task SubscribeEvent(EventSubscription subscription, IPersistencePr { //TODO: move to own class Logger.LogDebug("Subscribing to event {0} {1} for workflow {2} step {3}", subscription.EventName, subscription.EventKey, subscription.WorkflowId, subscription.StepId); - + await persistenceStore.CreateEventSubscription(subscription, cancellationToken); if (subscription.EventName != Event.EventTypeActivity) { @@ -101,13 +101,21 @@ private async Task SubscribeEvent(EventSubscription subscription, IPersistencePr while (!acquiredLock && attempt < 10) { acquiredLock = await _lockProvider.AcquireLock(eventKey, cancellationToken); - await Task.Delay(Options.IdleTime); + await Task.Delay(Options.IdleTime, cancellationToken); attempt++; } - await persistenceStore.MarkEventUnprocessed(evt, cancellationToken); - await QueueProvider.QueueWork(evt, QueueType.Event); + if (!acquiredLock) + { + Logger.LogWarning($"Failed to lock {evt}"); + } + else + { + await persistenceStore.MarkEventUnprocessed(evt, cancellationToken); + await QueueProvider.QueueWork(evt, QueueType.Event); + _greylist.Remove(eventKey); + } } finally { From 112fdee8eb59edb46c09d6ff743ceaa4d0c3daf4 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Fri, 2 Jul 2021 10:37:24 +0300 Subject: [PATCH 345/462] Improve delay, change greylist clean --- .../Services/BackgroundTasks/WorkflowConsumer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 23227d95d..6e5c1d3c1 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -35,10 +35,10 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance Logger.LogInformation("Workflow locked {0}", itemId); return; } - + WorkflowInstance workflow = null; WorkflowExecutorResult result = null; - + try { cancellationToken.ThrowIfCancellationRequested(); @@ -77,9 +77,9 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance } } } - + } - + private async Task SubscribeEvent(EventSubscription subscription, IPersistenceProvider persistenceStore, CancellationToken cancellationToken) { //TODO: move to own class @@ -100,8 +100,8 @@ private async Task SubscribeEvent(EventSubscription subscription, IPersistencePr int attempt = 0; while (!acquiredLock && attempt < 10) { - acquiredLock = await _lockProvider.AcquireLock(eventKey, cancellationToken); await Task.Delay(Options.IdleTime, cancellationToken); + acquiredLock = await _lockProvider.AcquireLock(eventKey, cancellationToken); attempt++; } @@ -112,9 +112,9 @@ private async Task SubscribeEvent(EventSubscription subscription, IPersistencePr } else { + _greylist.Remove(eventKey); await persistenceStore.MarkEventUnprocessed(evt, cancellationToken); await QueueProvider.QueueWork(evt, QueueType.Event); - _greylist.Remove(eventKey); } } finally From 782ca9aab7a1ca9eff312be0ede5dc9c0e484052 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Fri, 2 Jul 2021 10:59:25 +0300 Subject: [PATCH 346/462] Adding unit test to cover Parallel event scenario --- .../Scenarios/ParallelEventsScenario.cs | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/ParallelEventsScenario.cs diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ParallelEventsScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ParallelEventsScenario.cs new file mode 100644 index 000000000..d7176c880 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ParallelEventsScenario.cs @@ -0,0 +1,91 @@ +using System; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using WorkflowCore.Testing; +using System.Threading; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public sealed class ParallelEventsScenario + : WorkflowTest + { + private const string EVENT_KEY = nameof(EVENT_KEY); + + public class MyDataClass + { + public string StrValue1 { get; set; } + public string StrValue2 { get; set; } + } + + public class ParallelEventsWorkflow : IWorkflow + { + public string Id => "EventWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => ExecutionResult.Next()) + .Parallel() + .Do(then => + then.WaitFor("Event1", data => EVENT_KEY).Then(x => + { + Thread.Sleep(300); + return ExecutionResult.Next(); + })) + .Do(then => + then.WaitFor("Event2", data => EVENT_KEY).Then(x => + { + Thread.Sleep(100); + return ExecutionResult.Next(); + })) + .Do(then => + then.WaitFor("Event3", data => EVENT_KEY).Then(x => + { + Thread.Sleep(1000); + return ExecutionResult.Next(); + })) + .Do(then => + then.WaitFor("Event4", data => EVENT_KEY).Then(x => + { + Thread.Sleep(100); + return ExecutionResult.Next(); + })) + .Do(then => + then.WaitFor("Event5", data => EVENT_KEY).Then(x => + { + Thread.Sleep(100); + return ExecutionResult.Next(); + })) + .Join() + .Then(x => + { + return ExecutionResult.Next(); + }); + } + } + + public ParallelEventsScenario() + { + Setup(); + } + + [Fact] + public void Scenario() + { + var eventKey = Guid.NewGuid().ToString(); + var workflowId = StartWorkflow(new MyDataClass { StrValue1 = eventKey, StrValue2 = eventKey }); + Host.PublishEvent("Event1", EVENT_KEY, "Pass1"); + Host.PublishEvent("Event2", EVENT_KEY, "Pass2"); + Host.PublishEvent("Event3", EVENT_KEY, "Pass3"); + Host.PublishEvent("Event4", EVENT_KEY, "Pass4"); + Host.PublishEvent("Event5", EVENT_KEY, "Pass5"); + + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + } + } +} From f049bd5c1bb77784c36fa192acccf3d5f3a0b652 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 2 Jul 2021 19:58:37 -0700 Subject: [PATCH 347/462] bump versions --- src/Directory.Build.props | 8 ++++---- .../WorkflowCore.Sample01/HelloWorldWorkflow.cs | 2 ++ test/WorkflowCore.Testing/WorkflowCore.Testing.csproj | 7 +++---- test/WorkflowCore.Testing/WorkflowTest.cs | 10 ---------- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index f8f086812..acd8f26db 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.5.0 - 3.5.0.0 - 3.5.0.0 + 3.5.1 + 3.5.1.0 + 3.5.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.5.0 + 3.5.1 diff --git a/src/samples/WorkflowCore.Sample01/HelloWorldWorkflow.cs b/src/samples/WorkflowCore.Sample01/HelloWorldWorkflow.cs index f268b6307..157966f18 100644 --- a/src/samples/WorkflowCore.Sample01/HelloWorldWorkflow.cs +++ b/src/samples/WorkflowCore.Sample01/HelloWorldWorkflow.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using WorkflowCore.Interface; +using WorkflowCore.Models; using WorkflowCore.Sample01.Steps; namespace WorkflowCore.Sample01 @@ -10,6 +11,7 @@ public class HelloWorldWorkflow : IWorkflow public void Build(IWorkflowBuilder builder) { builder + .UseDefaultErrorBehavior(WorkflowErrorHandling.Suspend) .StartWith() .Then(); } diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 4449e3fa6..9b89e8d60 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 3.5.0 - 3.5.0.0 - 3.5.0.0 + 3.5.1 + 3.5.1.0 + 3.5.1.0 Facilitates testing of workflows built on Workflow-Core @@ -12,7 +12,6 @@ - diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/test/WorkflowCore.Testing/WorkflowTest.cs index 84db06002..7bd3587c2 100644 --- a/test/WorkflowCore.Testing/WorkflowTest.cs +++ b/test/WorkflowCore.Testing/WorkflowTest.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using MongoDB.Bson.Serialization; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -28,15 +27,6 @@ protected virtual void Setup(bool registerClassMap = false) var serviceProvider = services.BuildServiceProvider(); - //config logging - var loggerFactory = serviceProvider.GetService(); - //loggerFactory.AddConsole(LogLevel.Debug); - - if (registerClassMap && !BsonClassMap.IsClassMapRegistered(typeof(TData))) - { - BsonClassMap.RegisterClassMap(map => map.AutoMap()); - } - PersistenceProvider = serviceProvider.GetService(); Host = serviceProvider.GetService(); Host.RegisterWorkflow(); From 06470ce83446a970ff95f2f070793cefa7f6e5cc Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 2 Jul 2021 20:13:56 -0700 Subject: [PATCH 348/462] remove mongo dependency from testing package --- src/samples/WorkflowCore.TestSample01/NUnitTest.cs | 2 +- .../Scenarios/DataIOScenario.cs | 2 +- test/WorkflowCore.Testing/WorkflowCore.Testing.csproj | 6 +++--- test/WorkflowCore.Testing/WorkflowTest.cs | 2 +- .../Scenarios/MongoDataScenario.cs | 8 +++++++- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/samples/WorkflowCore.TestSample01/NUnitTest.cs b/src/samples/WorkflowCore.TestSample01/NUnitTest.cs index 91be04202..167c97441 100644 --- a/src/samples/WorkflowCore.TestSample01/NUnitTest.cs +++ b/src/samples/WorkflowCore.TestSample01/NUnitTest.cs @@ -13,7 +13,7 @@ public class NUnitTest : WorkflowTest [SetUp] protected void Setup() { - base.Setup(false); + base.Setup(); } [Test] diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs index d8a12724d..1d4157c96 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DataIOScenario.cs @@ -53,7 +53,7 @@ public void Build(IWorkflowBuilder builder) public DataIOScenario() { - Setup(true); + Setup(); } [Fact] diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj index 9b89e8d60..6102c9892 100644 --- a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj +++ b/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 3.5.1 - 3.5.1.0 - 3.5.1.0 + 3.5.2 + 3.5.2.0 + 3.5.2.0 Facilitates testing of workflows built on Workflow-Core diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/test/WorkflowCore.Testing/WorkflowTest.cs index 7bd3587c2..46a6d0ce8 100644 --- a/test/WorkflowCore.Testing/WorkflowTest.cs +++ b/test/WorkflowCore.Testing/WorkflowTest.cs @@ -18,7 +18,7 @@ public abstract class WorkflowTest : IDisposable protected IPersistenceProvider PersistenceProvider; protected List UnhandledStepErrors = new List(); - protected virtual void Setup(bool registerClassMap = false) + protected virtual void Setup() { //setup dependency injection IServiceCollection services = new ServiceCollection(); diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs index 468915f75..e842f6fee 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs @@ -1,5 +1,6 @@ using System; using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson.Serialization; using WorkflowCore.IntegrationTests.Scenarios; using Xunit; @@ -7,7 +8,12 @@ namespace WorkflowCore.Tests.MongoDB.Scenarios { [Collection("Mongo collection")] public class MongoDataScenario : DataIOScenario - { + { + public MongoDataScenario() : base() + { + BsonClassMap.RegisterClassMap(map => map.AutoMap()); + } + protected override void ConfigureServices(IServiceCollection services) { services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); From a94f0b1875fbe7938ef073ee4e602fe8cb92486c Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 6 Jul 2021 13:52:29 +0200 Subject: [PATCH 349/462] Allow mongo client configuration --- .../ServiceCollectionExtensions.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs index 34e7857c8..87b0e689e 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs @@ -9,17 +9,25 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { - public static WorkflowOptions UseMongoDB(this WorkflowOptions options, string mongoUrl, string databaseName) + public static WorkflowOptions UseMongoDB( + this WorkflowOptions options, + string mongoUrl, + string databaseName, + Action configureClient = default) { options.UsePersistence(sp => { - var client = new MongoClient(mongoUrl); + var mongoClientSettings = MongoClientSettings.FromConnectionString(mongoUrl); + configureClient?.Invoke(mongoClientSettings); + var client = new MongoClient(mongoClientSettings); var db = client.GetDatabase(databaseName); return new MongoPersistenceProvider(db); }); options.Services.AddTransient(sp => { - var client = new MongoClient(mongoUrl); + var mongoClientSettings = MongoClientSettings.FromConnectionString(mongoUrl); + configureClient?.Invoke(mongoClientSettings); + var client = new MongoClient(mongoClientSettings); var db = client.GetDatabase(databaseName); return new WorkflowPurger(db); }); From b194abe80449c96afee0db6f90b79b88db42d7ae Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Sun, 11 Jul 2021 12:56:14 +0300 Subject: [PATCH 350/462] Add AzureWorkflow purger --- src/WorkflowCore/Interface/IWorkflowPurger.cs | 3 +- .../Services/WorkflowPurger.cs | 7 ++-- .../Services/WorkflowPurger.cs | 8 ++-- .../Services/WorkflowPurger.cs | 10 ++--- .../ServiceCollectionExtensions.cs | 2 + .../Services/WorkflowPurger.cs | 42 +++++++++++++++++++ 6 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 src/providers/WorkflowCore.Providers.Azure/Services/WorkflowPurger.cs diff --git a/src/WorkflowCore/Interface/IWorkflowPurger.cs b/src/WorkflowCore/Interface/IWorkflowPurger.cs index a85d9a0bf..42f6ba85f 100644 --- a/src/WorkflowCore/Interface/IWorkflowPurger.cs +++ b/src/WorkflowCore/Interface/IWorkflowPurger.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using WorkflowCore.Models; @@ -6,6 +7,6 @@ namespace WorkflowCore.Interface { public interface IWorkflowPurger { - Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan); + Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs index 31384be84..904895809 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowPurger.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using WorkflowCore.Interface; @@ -18,12 +19,12 @@ public WorkflowPurger(IWorkflowDbContextFactory contextFactory) _contextFactory = contextFactory; } - public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan) + public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan, CancellationToken cancellationToken = default) { var olderThanUtc = olderThan.ToUniversalTime(); using (var db = ConstructDbContext()) { - var workflows = await db.Set().Where(x => x.Status == status && x.CompleteTime < olderThanUtc).ToListAsync(); + var workflows = await db.Set().Where(x => x.Status == status && x.CompleteTime < olderThanUtc).ToListAsync(cancellationToken); foreach (var wf in workflows) { foreach (var pointer in wf.ExecutionPointers) @@ -38,7 +39,7 @@ public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan) db.Remove(wf); } - await db.SaveChangesAsync(); + await db.SaveChangesAsync(cancellationToken); } } diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/WorkflowPurger.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/WorkflowPurger.cs index 85b1be31b..56343d3f6 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/WorkflowPurger.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/WorkflowPurger.cs @@ -3,24 +3,26 @@ using MongoDB.Driver; using WorkflowCore.Models; using WorkflowCore.Interface; +using System.Threading; namespace WorkflowCore.Persistence.MongoDB.Services { public class WorkflowPurger : IWorkflowPurger { private readonly IMongoDatabase _database; - private IMongoCollection WorkflowInstances => _database.GetCollection(MongoPersistenceProvider.WorkflowCollectionName); + private IMongoCollection WorkflowInstances => _database.GetCollection(MongoPersistenceProvider.WorkflowCollectionName); public WorkflowPurger(IMongoDatabase database) { _database = database; } - public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan) + public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan, CancellationToken cancellationToken = default) { var olderThanUtc = olderThan.ToUniversalTime(); - await WorkflowInstances.DeleteManyAsync(x => x.Status == status && x.CompleteTime < olderThanUtc); + await WorkflowInstances.DeleteManyAsync(x => x.Status == status + && x.CompleteTime < olderThanUtc, cancellationToken); } } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs b/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs index f799fd6a5..c70fc09fd 100644 --- a/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs +++ b/src/providers/WorkflowCore.Persistence.RavenDB/Services/WorkflowPurger.cs @@ -3,9 +3,9 @@ using WorkflowCore.Models; using System.Threading.Tasks; using Raven.Client.Documents; -using System.Linq; using Raven.Client.Documents.Operations; using Raven.Client.Documents.Queries; +using System.Threading; namespace WorkflowCore.Persistence.RavenDB.Services { @@ -18,9 +18,9 @@ public WorkflowPurger(IDocumentStore database) _database = database; } - public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan) + public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan, CancellationToken cancellationToken = default) { - await DeleteWorkflowInstances(status, olderThan); + await DeleteWorkflowInstances(status, olderThan, cancellationToken); } @@ -28,11 +28,11 @@ public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan) /// Delete all Workflow Documents /// /// - private Task DeleteWorkflowInstances(WorkflowStatus status, DateTime olderThan) + private Task DeleteWorkflowInstances(WorkflowStatus status, DateTime olderThan, CancellationToken cancellationToken = default) { var utcTime = olderThan.ToUniversalTime(); var queryToDelete = new IndexQuery { Query = $"FROM {nameof(WorkflowInstance)} where status = '{status}' and CompleteTime < '{olderThan}'" }; - return _database.Operations.SendAsync(new DeleteByQueryOperation(queryToDelete, new QueryOperationOptions { AllowStale = false })); + return _database.Operations.SendAsync(new DeleteByQueryOperation(queryToDelete, new QueryOperationOptions { AllowStale = false }), token: cancellationToken); } } } diff --git a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs index 92718cb95..897726e82 100644 --- a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Providers.Azure.Interface; using WorkflowCore.Providers.Azure.Services; @@ -39,6 +40,7 @@ public static WorkflowOptions UseCosmosDbPersistence( options.Services.AddSingleton(sp => new CosmosClientFactory(connectionString)); options.Services.AddTransient(sp => new CosmosDbProvisioner(sp.GetService(), cosmosDbStorageOptions)); + options.Services.AddTransient(sp => new WorkflowPurger(sp.GetService(), databaseId, cosmosDbStorageOptions)); options.UsePersistence(sp => new CosmosDbPersistenceProvider(sp.GetService(), databaseId, sp.GetService(), cosmosDbStorageOptions)); return options; } diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/WorkflowPurger.cs b/src/providers/WorkflowCore.Providers.Azure/Services/WorkflowPurger.cs new file mode 100644 index 000000000..64050f9f8 --- /dev/null +++ b/src/providers/WorkflowCore.Providers.Azure/Services/WorkflowPurger.cs @@ -0,0 +1,42 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Azure.Cosmos; +using Microsoft.Azure.Cosmos.Linq; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Providers.Azure.Interface; +using WorkflowCore.Providers.Azure.Models; + +namespace WorkflowCore.Providers.Azure.Services +{ + public class WorkflowPurger : IWorkflowPurger + { + private readonly Lazy _workflowContainer; + + public WorkflowPurger(ICosmosClientFactory clientFactory, string dbId, CosmosDbStorageOptions cosmosDbStorageOptions) + { + _workflowContainer = new Lazy(() => clientFactory.GetCosmosClient() + .GetDatabase(dbId) + .GetContainer(cosmosDbStorageOptions.WorkflowContainerName)); + } + + public async Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan, CancellationToken cancellationToken = default) + { + var olderThanUtc = olderThan.ToUniversalTime(); + using (FeedIterator feedIterator = _workflowContainer.Value.GetItemLinqQueryable() + .Where(x => x.Status == status && x.CompleteTime < olderThanUtc) + .ToFeedIterator()) + { + while (feedIterator.HasMoreResults) + { + foreach (var item in await feedIterator.ReadNextAsync(cancellationToken)) + { + await _workflowContainer.Value.DeleteItemAsync(item.id, new PartitionKey(item.id), cancellationToken: cancellationToken); + } + } + } + } + } +} From 2ff52820e9f58e8884034c370174e86f285f2f07 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Sun, 11 Jul 2021 12:59:25 +0300 Subject: [PATCH 351/462] Rollback changes for Even subscription --- .../Services/BackgroundTasks/WorkflowConsumer.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 2faba7b87..7c0637482 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -89,19 +89,8 @@ private async Task SubscribeEvent(EventSubscription subscription, IPersistencePr if (subscription.EventName != Event.EventTypeActivity) { var events = await persistenceStore.GetEvents(subscription.EventName, subscription.EventKey, subscription.SubscribeAsOf, cancellationToken); - foreach (var evt in events) { - var locked = await _lockProvider.AcquireLock($"evt:{evt}", cancellationToken); - int attempt = 0; - while (locked && attempt < 10) - { - locked = await _lockProvider.AcquireLock($"evt:{evt}", cancellationToken); - await Task.Delay(Options.IdleTime); - - attempt++; - } - await persistenceStore.MarkEventUnprocessed(evt, cancellationToken); await QueueProvider.QueueWork(evt, QueueType.Event); } From 5d890c069693fde7527458880d95fe70afb612c5 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Sun, 11 Jul 2021 18:08:15 +0300 Subject: [PATCH 352/462] Change Scope for WorkflowPurger --- .../WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs index 897726e82..0aa1963e4 100644 --- a/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Azure/ServiceCollectionExtensions.cs @@ -40,7 +40,7 @@ public static WorkflowOptions UseCosmosDbPersistence( options.Services.AddSingleton(sp => new CosmosClientFactory(connectionString)); options.Services.AddTransient(sp => new CosmosDbProvisioner(sp.GetService(), cosmosDbStorageOptions)); - options.Services.AddTransient(sp => new WorkflowPurger(sp.GetService(), databaseId, cosmosDbStorageOptions)); + options.Services.AddSingleton(sp => new WorkflowPurger(sp.GetService(), databaseId, cosmosDbStorageOptions)); options.UsePersistence(sp => new CosmosDbPersistenceProvider(sp.GetService(), databaseId, sp.GetService(), cosmosDbStorageOptions)); return options; } From ed0b7417d86c7d3931dd86be85cb1e5b612fb5ec Mon Sep 17 00:00:00 2001 From: slee_dtec Date: Thu, 15 Jul 2021 14:56:33 -0400 Subject: [PATCH 353/462] fix issue871 where child steps in the failed Saga container do not get retried when the suspended workflow is resumed --- src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs index 20ff0bab3..3cc3279ec 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs @@ -29,6 +29,8 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version }); + + step.PrimeForRetry(pointer); } } } From d6056150dd910798d6ec4f325e85f8312a132889 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 29 Jul 2021 22:06:29 -0700 Subject: [PATCH 354/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index acd8f26db..a02261928 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.5.1 - 3.5.1.0 - 3.5.1.0 + 3.5.2 + 3.5.2.0 + 3.5.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.5.1 + 3.5.2 From e85cdf318f5e003cf60d21cc001a7618bb82b2ae Mon Sep 17 00:00:00 2001 From: lyzerk Date: Fri, 30 Jul 2021 16:22:30 +0300 Subject: [PATCH 355/462] disable sample20 --- src/samples/WorkflowCore.Sample20/Program.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/samples/WorkflowCore.Sample20/Program.cs b/src/samples/WorkflowCore.Sample20/Program.cs index 69009fe98..4e8f1303f 100644 --- a/src/samples/WorkflowCore.Sample20/Program.cs +++ b/src/samples/WorkflowCore.Sample20/Program.cs @@ -9,7 +9,8 @@ class Program { static void Main(string[] args) { - var serviceProvider = ConfigureServices(); + // THE TEST DISABLED FOR THE BUILD TIME LIMIT + /*var serviceProvider = ConfigureServices(); //start the workflow host var host = serviceProvider.GetService(); @@ -29,7 +30,7 @@ static void Main(string[] args) } Console.ReadLine(); - host.Stop(); + host.Stop();*/ } private static IServiceProvider ConfigureServices() From 8082c624632775de1f66552ac755b5d6ea7739b8 Mon Sep 17 00:00:00 2001 From: lyzerk Date: Fri, 30 Jul 2021 17:04:31 +0300 Subject: [PATCH 356/462] remove sample20 --- WorkflowCore.sln | 9 +--- .../WorkflowCore.Sample20/ActivityWorkflow.cs | 29 ---------- src/samples/WorkflowCore.Sample20/Program.cs | 54 ------------------- src/samples/WorkflowCore.Sample20/README.md | 34 ------------ .../Steps/CustomMessage.cs | 19 ------- .../Steps/GoodbyeWorld.cs | 16 ------ .../WorkflowCore.Sample20/Steps/HelloWorld.cs | 16 ------ .../WorkflowCore.Sample20.csproj | 20 ------- 8 files changed, 1 insertion(+), 196 deletions(-) delete mode 100644 src/samples/WorkflowCore.Sample20/ActivityWorkflow.cs delete mode 100644 src/samples/WorkflowCore.Sample20/Program.cs delete mode 100644 src/samples/WorkflowCore.Sample20/README.md delete mode 100644 src/samples/WorkflowCore.Sample20/Steps/CustomMessage.cs delete mode 100644 src/samples/WorkflowCore.Sample20/Steps/GoodbyeWorld.cs delete mode 100644 src/samples/WorkflowCore.Sample20/Steps/HelloWorld.cs delete mode 100644 src/samples/WorkflowCore.Sample20/WorkflowCore.Sample20.csproj diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 8bd5d6ca1..de509ed05 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -150,9 +150,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.QueuePro EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample19", "src\samples\WorkflowCore.Sample19\WorkflowCore.Sample19.csproj", "{1223ED47-3E5E-4960-B70D-DFAF550F6666}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample20", "src\samples\WorkflowCore.Sample20\WorkflowCore.Sample20.csproj", "{68D1B955-1049-477B-A894-13FCB96C45DE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -376,10 +374,6 @@ Global {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Debug|Any CPU.Build.0 = Debug|Any CPU {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.Build.0 = Release|Any CPU - {68D1B955-1049-477B-A894-13FCB96C45DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68D1B955-1049-477B-A894-13FCB96C45DE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68D1B955-1049-477B-A894-13FCB96C45DE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68D1B955-1049-477B-A894-13FCB96C45DE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -442,7 +436,6 @@ Global {54DE20BA-EBA7-4BF0-9BD9-F03766849716} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB} {1223ED47-3E5E-4960-B70D-DFAF550F6666} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} {AF205715-C8B7-42EF-BF14-AFC9E7F27242} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2} - {68D1B955-1049-477B-A894-13FCB96C45DE} = {5080DB09-CBE8-4C45-9957-C3BB7651755E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4} diff --git a/src/samples/WorkflowCore.Sample20/ActivityWorkflow.cs b/src/samples/WorkflowCore.Sample20/ActivityWorkflow.cs deleted file mode 100644 index 03208dcfd..000000000 --- a/src/samples/WorkflowCore.Sample20/ActivityWorkflow.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using WorkflowCore.Interface; -using WorkflowCore.Sample20.Steps; - -namespace WorkflowCore.Sample20 -{ - class ActivityWorkflow : IWorkflow - { - public string Id => "activity-sample"; - public int Version => 1; - - public void Build(IWorkflowBuilder builder) - { - builder - .StartWith() - .Activity((data, context) => "get-approval-" + context.Workflow.Id, (data) => data.Request) - .Output(data => data.ApprovedBy, step => step.Result) - .Then() - .Input(step => step.Message, data => "Approved by " + data.ApprovedBy) - .Then(); - } - } - - class MyData - { - public string Request { get; set; } - public string ApprovedBy { get; set; } - } -} diff --git a/src/samples/WorkflowCore.Sample20/Program.cs b/src/samples/WorkflowCore.Sample20/Program.cs deleted file mode 100644 index 4e8f1303f..000000000 --- a/src/samples/WorkflowCore.Sample20/Program.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; -using WorkflowCore.Interface; - -namespace WorkflowCore.Sample20 -{ - class Program - { - static void Main(string[] args) - { - // THE TEST DISABLED FOR THE BUILD TIME LIMIT - /*var serviceProvider = ConfigureServices(); - - //start the workflow host - var host = serviceProvider.GetService(); - host.RegisterWorkflow(); - host.Start(); - - Console.WriteLine("Starting workflow..."); - - var workflowId = host.StartWorkflow("activity-sample", new MyData { Request = "Spend $1,000,000" }).Result; - - var approval = host.GetPendingActivity("get-approval-" + workflowId, "worker1", TimeSpan.FromMinutes(1)).Result; - - if (approval != null) - { - Console.WriteLine("Approval required for " + approval.Parameters); - host.SubmitActivitySuccess(approval.Token, "John Smith"); - } - - Console.ReadLine(); - host.Stop();*/ - } - - private static IServiceProvider ConfigureServices() - { - //setup dependency injection - IServiceCollection services = new ServiceCollection(); - //services.AddWorkflow(); - services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow")); - //services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true)); - //services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;", true, true)); - services.AddLogging(cfg => - { - cfg.AddConsole(); - cfg.AddDebug(); - }); - - var serviceProvider = services.BuildServiceProvider(); - return serviceProvider; - } - } -} diff --git a/src/samples/WorkflowCore.Sample20/README.md b/src/samples/WorkflowCore.Sample20/README.md deleted file mode 100644 index 46f5d1e22..000000000 --- a/src/samples/WorkflowCore.Sample20/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Activity sample - -Illustrates how to have your workflow wait for an external activity that is fulfilled by a worker that you implement. - -This workflow will wait for the `get-approval-{workflowId}` activity and pass the request string to it as an input. - -The main reason behind of this example is Activities are global listeners. Therefore, in some cases, you want to be sure about its uniqueness. - -Also, you can take look the issue https://github.com/danielgerlag/workflow-core/issues/542 - -```c# -builder - .StartWith() - .Activity((data, context) => context.Workflow.Id, (data) => data.Request) - .Output(data => data.ApprovedBy, step => step.Result) - .Then() - .Input(step => step.Message, data => "Approved by " + data.ApprovedBy) - .Then(); -``` - -Then we implement an activity worker to pull pending activities of type `get-approval`, where we can inspect the input and submit a response back to the waiting workflow. - -```c# -var workflowId = host.StartWorkflow("activity-sample", new MyData { Request = "Spend $1,000,000" }).Result; - -var approval = host.GetPendingActivity("get-approval-" + workflowId, "worker1", TimeSpan.FromMinutes(1)).Result; - -if (approval != null) -{ - Console.WriteLine("Approval required for " + approval.Parameters); - host.SubmitActivitySuccess(approval.Token, "John Smith"); -} -``` - diff --git a/src/samples/WorkflowCore.Sample20/Steps/CustomMessage.cs b/src/samples/WorkflowCore.Sample20/Steps/CustomMessage.cs deleted file mode 100644 index cad8eff61..000000000 --- a/src/samples/WorkflowCore.Sample20/Steps/CustomMessage.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Linq; -using WorkflowCore.Interface; -using WorkflowCore.Models; - -namespace WorkflowCore.Sample20.Steps -{ - public class CustomMessage : StepBody - { - - public string Message { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - Console.WriteLine(Message); - return ExecutionResult.Next(); - } - } -} diff --git a/src/samples/WorkflowCore.Sample20/Steps/GoodbyeWorld.cs b/src/samples/WorkflowCore.Sample20/Steps/GoodbyeWorld.cs deleted file mode 100644 index 0908966d9..000000000 --- a/src/samples/WorkflowCore.Sample20/Steps/GoodbyeWorld.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Linq; -using WorkflowCore.Interface; -using WorkflowCore.Models; - -namespace WorkflowCore.Sample20.Steps -{ - public class GoodbyeWorld : StepBody - { - public override ExecutionResult Run(IStepExecutionContext context) - { - Console.WriteLine("Goodbye world"); - return ExecutionResult.Next(); - } - } -} diff --git a/src/samples/WorkflowCore.Sample20/Steps/HelloWorld.cs b/src/samples/WorkflowCore.Sample20/Steps/HelloWorld.cs deleted file mode 100644 index 5623bf684..000000000 --- a/src/samples/WorkflowCore.Sample20/Steps/HelloWorld.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Linq; -using WorkflowCore.Interface; -using WorkflowCore.Models; - -namespace WorkflowCore.Sample20.Steps -{ - public class HelloWorld : StepBody - { - public override ExecutionResult Run(IStepExecutionContext context) - { - Console.WriteLine("Hello world"); - return ExecutionResult.Next(); - } - } -} diff --git a/src/samples/WorkflowCore.Sample20/WorkflowCore.Sample20.csproj b/src/samples/WorkflowCore.Sample20/WorkflowCore.Sample20.csproj deleted file mode 100644 index 660b47511..000000000 --- a/src/samples/WorkflowCore.Sample20/WorkflowCore.Sample20.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - Exe - netcoreapp3.0 - - - - - - - - - - - - - - - From 19c34cbe376f92737a18547b448a5959d889e129 Mon Sep 17 00:00:00 2001 From: lyzerk Date: Mon, 2 Aug 2021 08:32:16 +0300 Subject: [PATCH 357/462] sln fix --- WorkflowCore.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WorkflowCore.sln b/WorkflowCore.sln index de509ed05..be18fd721 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -150,7 +150,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.QueuePro EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample19", "src\samples\WorkflowCore.Sample19\WorkflowCore.Sample19.csproj", "{1223ED47-3E5E-4960-B70D-DFAF550F6666}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 078acc07e8d785aebf283d119a3cbd49b1013195 Mon Sep 17 00:00:00 2001 From: lyzerk Date: Mon, 2 Aug 2021 08:51:37 +0300 Subject: [PATCH 358/462] disable activity scenario2 --- .../Scenarios/ActivityScenario2.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario2.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario2.cs index 356f77402..1cf5b6330 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario2.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ActivityScenario2.cs @@ -8,6 +8,8 @@ namespace WorkflowCore.IntegrationTests.Scenarios { + /* + * DISABLED for bug on build pipeline public class ActivityScenario2 : WorkflowTest { public class MyDataClass @@ -71,5 +73,6 @@ public void Scenario() outData.Value1.Should().Be("a1"); outData.Value2.Should().Be(2); } - } + }*/ + } From f3042be3df9a3b032d6feed1fd60ba90db0328cf Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 2 Aug 2021 11:16:11 -0700 Subject: [PATCH 359/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index a02261928..fa0f334be 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.5.2 - 3.5.2.0 - 3.5.2.0 + 3.5.3 + 3.5.3.0 + 3.5.3.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.5.2 + 3.5.3 From cd9b0961cf96733a5851fa92a5b0114bca6a5f55 Mon Sep 17 00:00:00 2001 From: JRoger Date: Tue, 3 Aug 2021 09:38:07 +0800 Subject: [PATCH 360/462] feat: Compatible with net5.0 --- .github/workflows/dotnet.yml | 2 +- .../WorkflowCore.Persistence.MySQL/MysqlContext.cs | 4 ++++ .../WorkflowCore.Persistence.MySQL.csproj | 12 ++++++++++-- .../WorkflowCore.Sample04.csproj | 14 +++++++------- test/WorkflowCore.Tests.MySQL/DockerSetup.cs | 4 ++-- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 29100399e..12b398572 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.1.301 + dotnet-version: 5.0.* - name: Restore dependencies run: dotnet restore - name: Build diff --git a/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs index dbf4a8085..a6ebba9c2 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs @@ -21,7 +21,11 @@ public MysqlContext(string connectionString, Action builder) diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index af6cbc18d..e0e83315b 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -4,7 +4,7 @@ Workflow Core MySQL Persistence Provider 1.0.0 Daniel Gerlag - netstandard2.0 + netstandard2.0;netstandard2.1 WorkflowCore.Persistence.MySQL WorkflowCore.Persistence.MySQL workflow;.NET;Core;state machine;WorkflowCore;MySQL @@ -18,7 +18,7 @@ Provides support to persist workflows running on Workflow Core to a MySQL database. - + all runtime; build; native; contentfiles; analyzers @@ -26,6 +26,14 @@ + + + all + runtime; build; native; contentfiles; analyzers + + + + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index 96f0355c0..ddae958be 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -4,7 +4,7 @@ netcoreapp3.1 WorkflowCore.Sample04 Exe - WorkflowCore.Sample04 + WorkflowCore.Sample04 false false false @@ -25,16 +25,16 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs index f91909f9d..05758f4c1 100644 --- a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using Docker.Testify; using Xunit; -using MySql.Data.MySqlClient; using System; +using MySqlConnector; namespace WorkflowCore.Tests.MySQL { @@ -13,7 +13,7 @@ public class MysqlDockerSetup : DockerSetup public static string RootPassword => "rootpwd123"; public override TimeSpan TimeOut => TimeSpan.FromSeconds(60); - + public override string ImageName => "mysql"; public override IList EnvironmentVariables => new List { $"MYSQL_ROOT_PASSWORD={RootPassword}" From a38721861e408aae2a9f618e221e6c65619ee223 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Tue, 3 Aug 2021 12:43:16 +0300 Subject: [PATCH 361/462] Fix parallel events test --- .../Scenarios/ParallelEventsScenario.cs | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ParallelEventsScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ParallelEventsScenario.cs index d7176c880..2c3b6825f 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/ParallelEventsScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ParallelEventsScenario.cs @@ -4,7 +4,8 @@ using Xunit; using FluentAssertions; using WorkflowCore.Testing; -using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; namespace WorkflowCore.IntegrationTests.Scenarios { @@ -19,9 +20,21 @@ public class MyDataClass public string StrValue2 { get; set; } } + public class SomeTask : StepBodyAsync + { + public TimeSpan Delay { get; set; } + + public override async Task RunAsync(IStepExecutionContext context) + { + await Task.Delay(Delay); + + return ExecutionResult.Next(); + } + } + public class ParallelEventsWorkflow : IWorkflow { - public string Id => "EventWorkflow"; + public string Id => nameof(ParallelEventsScenario); public int Version => 1; public void Build(IWorkflowBuilder builder) { @@ -29,35 +42,20 @@ public void Build(IWorkflowBuilder builder) .StartWith(context => ExecutionResult.Next()) .Parallel() .Do(then => - then.WaitFor("Event1", data => EVENT_KEY).Then(x => - { - Thread.Sleep(300); - return ExecutionResult.Next(); - })) + then.WaitFor("Event1", data => EVENT_KEY).Then() + .Input(step => step.Delay, data => TimeSpan.FromMilliseconds(2000))) .Do(then => - then.WaitFor("Event2", data => EVENT_KEY).Then(x => - { - Thread.Sleep(100); - return ExecutionResult.Next(); - })) + then.WaitFor("Event2", data => EVENT_KEY).Then() + .Input(step => step.Delay, data => TimeSpan.FromMilliseconds(2000))) .Do(then => - then.WaitFor("Event3", data => EVENT_KEY).Then(x => - { - Thread.Sleep(1000); - return ExecutionResult.Next(); - })) + then.WaitFor("Event3", data => EVENT_KEY).Then() + .Input(step => step.Delay, data => TimeSpan.FromMilliseconds(5000))) .Do(then => - then.WaitFor("Event4", data => EVENT_KEY).Then(x => - { - Thread.Sleep(100); - return ExecutionResult.Next(); - })) + then.WaitFor("Event4", data => EVENT_KEY).Then() + .Input(step => step.Delay, data => TimeSpan.FromMilliseconds(100))) .Do(then => - then.WaitFor("Event5", data => EVENT_KEY).Then(x => - { - Thread.Sleep(100); - return ExecutionResult.Next(); - })) + then.WaitFor("Event5", data => EVENT_KEY).Then() + .Input(step => step.Delay, data => TimeSpan.FromMilliseconds(100))) .Join() .Then(x => { @@ -71,16 +69,21 @@ public ParallelEventsScenario() Setup(); } + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(s => s.UsePollInterval(TimeSpan.FromSeconds(1))); + } + [Fact] - public void Scenario() + public async Task Scenario() { var eventKey = Guid.NewGuid().ToString(); - var workflowId = StartWorkflow(new MyDataClass { StrValue1 = eventKey, StrValue2 = eventKey }); - Host.PublishEvent("Event1", EVENT_KEY, "Pass1"); - Host.PublishEvent("Event2", EVENT_KEY, "Pass2"); - Host.PublishEvent("Event3", EVENT_KEY, "Pass3"); - Host.PublishEvent("Event4", EVENT_KEY, "Pass4"); - Host.PublishEvent("Event5", EVENT_KEY, "Pass5"); + var workflowId = await StartWorkflowAsync(new MyDataClass { StrValue1 = eventKey, StrValue2 = eventKey }); + await Host.PublishEvent("Event1", EVENT_KEY, "Pass1"); + await Host.PublishEvent("Event2", EVENT_KEY, "Pass2"); + await Host.PublishEvent("Event3", EVENT_KEY, "Pass3"); + await Host.PublishEvent("Event4", EVENT_KEY, "Pass4"); + await Host.PublishEvent("Event5", EVENT_KEY, "Pass5"); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); From b43785b03acede2a670541298edd805e0e461e6d Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Tue, 3 Aug 2021 18:50:25 +0300 Subject: [PATCH 362/462] Allow to disable events publisher --- src/WorkflowCore/Models/WorkflowOptions.cs | 1 + src/WorkflowCore/ServiceCollectionExtensions.cs | 13 ++++++++++--- .../Services/ErrorHandlers/CompensateHandler.cs | 4 +--- .../Services/ErrorHandlers/SuspendHandler.cs | 2 +- .../Services/ErrorHandlers/TerminateHandler.cs | 2 +- .../Services/ExecutionResultProcessor.cs | 4 ++-- src/WorkflowCore/Services/WorkflowExecutor.cs | 4 ++-- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index bf2d28e06..fb1ae9a7e 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -38,6 +38,7 @@ public WorkflowOptions(IServiceCollection services) public bool EnableEvents { get; set; } = true; public bool EnableIndexes { get; set; } = true; public bool EnablePolling { get; set; } = true; + public bool EnableLifeCycleEventsPublisher { get; set; } = true; public void UsePersistence(Func factory) { diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 760a89d41..3ba7de642 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -31,7 +31,16 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(); services.AddSingleton(options); - services.AddSingleton(); + + if (options.EnableLifeCycleEventsPublisher) + { + services.AddSingleton(); + services.AddTransient(sp => sp.GetService()); + } + else + { + services.AddTransient(provider => null); + } if (options.EnableWorkflows) { @@ -53,8 +62,6 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddTransient(); } - services.AddTransient(sp => sp.GetService()); - services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index 028f0d112..d8990c160 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -8,17 +8,15 @@ namespace WorkflowCore.Services.ErrorHandlers { public class CompensateHandler : IWorkflowErrorHandler { - private readonly ILifeCycleEventPublisher _eventPublisher; private readonly IExecutionPointerFactory _pointerFactory; private readonly IDateTimeProvider _datetimeProvider; private readonly WorkflowOptions _options; public WorkflowErrorHandling Type => WorkflowErrorHandling.Compensate; - public CompensateHandler(IExecutionPointerFactory pointerFactory, ILifeCycleEventPublisher eventPublisher, IDateTimeProvider datetimeProvider, WorkflowOptions options) + public CompensateHandler(IExecutionPointerFactory pointerFactory, IDateTimeProvider datetimeProvider, WorkflowOptions options) { _pointerFactory = pointerFactory; - _eventPublisher = eventPublisher; _datetimeProvider = datetimeProvider; _options = options; } diff --git a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs index 3cc3279ec..8ff8fa35b 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs @@ -21,7 +21,7 @@ public SuspendHandler(ILifeCycleEventPublisher eventPublisher, IDateTimeProvider public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue bubbleUpQueue) { workflow.Status = WorkflowStatus.Suspended; - _eventPublisher.PublishNotification(new WorkflowSuspended + _eventPublisher?.PublishNotification(new WorkflowSuspended { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, diff --git a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs index 6cafe5ece..a3cc566fd 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs @@ -23,7 +23,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP workflow.Status = WorkflowStatus.Terminated; workflow.CompleteTime = _dateTimeProvider.UtcNow; - _eventPublisher.PublishNotification(new WorkflowTerminated + _eventPublisher?.PublishNotification(new WorkflowTerminated { EventTimeUtc = _dateTimeProvider.UtcNow, Reference = workflow.Reference, diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index 3c684945f..89f21f074 100755 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -77,7 +77,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition subsequent.Active = true; } - _eventPublisher.PublishNotification(new StepCompleted + _eventPublisher?.PublishNotification(new StepCompleted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, @@ -102,7 +102,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception) { - _eventPublisher.PublishNotification(new WorkflowError + _eventPublisher?.PublishNotification(new WorkflowError { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index ebdc818fe..8fa0101a0 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -123,7 +123,7 @@ private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, Workfl if (pointer.Status != PointerStatus.Running) { pointer.Status = PointerStatus.Running; - _publisher.PublishNotification(new StepStarted + _publisher?.PublishNotification(new StepStarted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, @@ -264,7 +264,7 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo await middlewareRunner.RunPostMiddleware(workflow, def); } - _publisher.PublishNotification(new WorkflowCompleted + _publisher?.PublishNotification(new WorkflowCompleted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, From 48ba0e0253d5210610f62f4a27db31c2dd494d86 Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Tue, 10 Aug 2021 11:03:02 +0300 Subject: [PATCH 363/462] Revert "Allow to disable events publisher" This reverts commit b43785b03acede2a670541298edd805e0e461e6d. --- src/WorkflowCore/Models/WorkflowOptions.cs | 1 - src/WorkflowCore/ServiceCollectionExtensions.cs | 13 +++---------- .../Services/ErrorHandlers/CompensateHandler.cs | 4 +++- .../Services/ErrorHandlers/SuspendHandler.cs | 2 +- .../Services/ErrorHandlers/TerminateHandler.cs | 2 +- .../Services/ExecutionResultProcessor.cs | 4 ++-- src/WorkflowCore/Services/WorkflowExecutor.cs | 4 ++-- 7 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index fb1ae9a7e..bf2d28e06 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -38,7 +38,6 @@ public WorkflowOptions(IServiceCollection services) public bool EnableEvents { get; set; } = true; public bool EnableIndexes { get; set; } = true; public bool EnablePolling { get; set; } = true; - public bool EnableLifeCycleEventsPublisher { get; set; } = true; public void UsePersistence(Func factory) { diff --git a/src/WorkflowCore/ServiceCollectionExtensions.cs b/src/WorkflowCore/ServiceCollectionExtensions.cs index 3ba7de642..760a89d41 100644 --- a/src/WorkflowCore/ServiceCollectionExtensions.cs +++ b/src/WorkflowCore/ServiceCollectionExtensions.cs @@ -31,16 +31,7 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddSingleton(); services.AddSingleton(options); - - if (options.EnableLifeCycleEventsPublisher) - { - services.AddSingleton(); - services.AddTransient(sp => sp.GetService()); - } - else - { - services.AddTransient(provider => null); - } + services.AddSingleton(); if (options.EnableWorkflows) { @@ -62,6 +53,8 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A services.AddTransient(); } + services.AddTransient(sp => sp.GetService()); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index d8990c160..028f0d112 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -8,15 +8,17 @@ namespace WorkflowCore.Services.ErrorHandlers { public class CompensateHandler : IWorkflowErrorHandler { + private readonly ILifeCycleEventPublisher _eventPublisher; private readonly IExecutionPointerFactory _pointerFactory; private readonly IDateTimeProvider _datetimeProvider; private readonly WorkflowOptions _options; public WorkflowErrorHandling Type => WorkflowErrorHandling.Compensate; - public CompensateHandler(IExecutionPointerFactory pointerFactory, IDateTimeProvider datetimeProvider, WorkflowOptions options) + public CompensateHandler(IExecutionPointerFactory pointerFactory, ILifeCycleEventPublisher eventPublisher, IDateTimeProvider datetimeProvider, WorkflowOptions options) { _pointerFactory = pointerFactory; + _eventPublisher = eventPublisher; _datetimeProvider = datetimeProvider; _options = options; } diff --git a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs index 8ff8fa35b..3cc3279ec 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/SuspendHandler.cs @@ -21,7 +21,7 @@ public SuspendHandler(ILifeCycleEventPublisher eventPublisher, IDateTimeProvider public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue bubbleUpQueue) { workflow.Status = WorkflowStatus.Suspended; - _eventPublisher?.PublishNotification(new WorkflowSuspended + _eventPublisher.PublishNotification(new WorkflowSuspended { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, diff --git a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs index a3cc566fd..6cafe5ece 100755 --- a/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/TerminateHandler.cs @@ -23,7 +23,7 @@ public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionP workflow.Status = WorkflowStatus.Terminated; workflow.CompleteTime = _dateTimeProvider.UtcNow; - _eventPublisher?.PublishNotification(new WorkflowTerminated + _eventPublisher.PublishNotification(new WorkflowTerminated { EventTimeUtc = _dateTimeProvider.UtcNow, Reference = workflow.Reference, diff --git a/src/WorkflowCore/Services/ExecutionResultProcessor.cs b/src/WorkflowCore/Services/ExecutionResultProcessor.cs index 89f21f074..3c684945f 100755 --- a/src/WorkflowCore/Services/ExecutionResultProcessor.cs +++ b/src/WorkflowCore/Services/ExecutionResultProcessor.cs @@ -77,7 +77,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition subsequent.Active = true; } - _eventPublisher?.PublishNotification(new StepCompleted + _eventPublisher.PublishNotification(new StepCompleted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, @@ -102,7 +102,7 @@ public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception) { - _eventPublisher?.PublishNotification(new WorkflowError + _eventPublisher.PublishNotification(new WorkflowError { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 8fa0101a0..ebdc818fe 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -123,7 +123,7 @@ private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, Workfl if (pointer.Status != PointerStatus.Running) { pointer.Status = PointerStatus.Running; - _publisher?.PublishNotification(new StepStarted + _publisher.PublishNotification(new StepStarted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, @@ -264,7 +264,7 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo await middlewareRunner.RunPostMiddleware(workflow, def); } - _publisher?.PublishNotification(new WorkflowCompleted + _publisher.PublishNotification(new WorkflowCompleted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, From 133a7feac08e7a0cc4d6bd61d717429e01c9b7cd Mon Sep 17 00:00:00 2001 From: Viktor Kryzhanovskyi Date: Tue, 10 Aug 2021 12:04:07 +0300 Subject: [PATCH 364/462] Changes after code review from Daniel --- src/WorkflowCore/Models/WorkflowOptions.cs | 1 + .../ErrorHandlers/CompensateHandler.cs | 4 +- .../Services/LifeCycleEventPublisher.cs | 7 ++- .../Services/LifeCycleEventPublisherTests.cs | 49 +++++++++++++++++-- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index bf2d28e06..fb1ae9a7e 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -38,6 +38,7 @@ public WorkflowOptions(IServiceCollection services) public bool EnableEvents { get; set; } = true; public bool EnableIndexes { get; set; } = true; public bool EnablePolling { get; set; } = true; + public bool EnableLifeCycleEventsPublisher { get; set; } = true; public void UsePersistence(Func factory) { diff --git a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs index 028f0d112..d8990c160 100644 --- a/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs +++ b/src/WorkflowCore/Services/ErrorHandlers/CompensateHandler.cs @@ -8,17 +8,15 @@ namespace WorkflowCore.Services.ErrorHandlers { public class CompensateHandler : IWorkflowErrorHandler { - private readonly ILifeCycleEventPublisher _eventPublisher; private readonly IExecutionPointerFactory _pointerFactory; private readonly IDateTimeProvider _datetimeProvider; private readonly WorkflowOptions _options; public WorkflowErrorHandling Type => WorkflowErrorHandling.Compensate; - public CompensateHandler(IExecutionPointerFactory pointerFactory, ILifeCycleEventPublisher eventPublisher, IDateTimeProvider datetimeProvider, WorkflowOptions options) + public CompensateHandler(IExecutionPointerFactory pointerFactory, IDateTimeProvider datetimeProvider, WorkflowOptions options) { _pointerFactory = pointerFactory; - _eventPublisher = eventPublisher; _datetimeProvider = datetimeProvider; _options = options; } diff --git a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs index 1dce43e4c..e73c6bf4f 100644 --- a/src/WorkflowCore/Services/LifeCycleEventPublisher.cs +++ b/src/WorkflowCore/Services/LifeCycleEventPublisher.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Threading.Tasks; using WorkflowCore.Interface; +using WorkflowCore.Models; using WorkflowCore.Models.LifeCycleEvents; namespace WorkflowCore.Services @@ -10,20 +11,22 @@ namespace WorkflowCore.Services public class LifeCycleEventPublisher : ILifeCycleEventPublisher, IDisposable { private readonly ILifeCycleEventHub _eventHub; + private readonly WorkflowOptions _workflowOptions; private readonly ILogger _logger; private BlockingCollection _outbox; private Task _dispatchTask; - public LifeCycleEventPublisher(ILifeCycleEventHub eventHub, ILoggerFactory loggerFactory) + public LifeCycleEventPublisher(ILifeCycleEventHub eventHub, WorkflowOptions workflowOptions, ILoggerFactory loggerFactory) { _eventHub = eventHub; + _workflowOptions = workflowOptions; _outbox = new BlockingCollection(); _logger = loggerFactory.CreateLogger(GetType()); } public void PublishNotification(LifeCycleEvent evt) { - if (_outbox.IsAddingCompleted) + if (_outbox.IsAddingCompleted || !_workflowOptions.EnableLifeCycleEventsPublisher) return; _outbox.Add(evt); diff --git a/test/WorkflowCore.UnitTests/Services/LifeCycleEventPublisherTests.cs b/test/WorkflowCore.UnitTests/Services/LifeCycleEventPublisherTests.cs index bfee3ad07..d8e38437e 100644 --- a/test/WorkflowCore.UnitTests/Services/LifeCycleEventPublisherTests.cs +++ b/test/WorkflowCore.UnitTests/Services/LifeCycleEventPublisherTests.cs @@ -1,8 +1,9 @@ -using System; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Moq; using WorkflowCore.Interface; +using WorkflowCore.Models; using WorkflowCore.Models.LifeCycleEvents; using WorkflowCore.Services; using Xunit; @@ -17,10 +18,17 @@ public async Task PublishNotification_WhenStarted_PublishesNotification() // Arrange var wasCalled = new TaskCompletionSource(); var eventHubMock = new Mock(); + var serviceCollectionMock = new Mock(); + + var workflowOptions = new WorkflowOptions(serviceCollectionMock.Object) + { + EnableLifeCycleEventsPublisher = true + }; + eventHubMock .Setup(hub => hub.PublishNotification(It.IsAny())) .Callback(() => wasCalled.SetResult(true)); - LifeCycleEventPublisher publisher = new LifeCycleEventPublisher(eventHubMock.Object, new LoggerFactory()); + LifeCycleEventPublisher publisher = new LifeCycleEventPublisher(eventHubMock.Object, workflowOptions, new LoggerFactory()); // Act publisher.Start(); @@ -28,6 +36,7 @@ public async Task PublishNotification_WhenStarted_PublishesNotification() // Assert await wasCalled.Task; + eventHubMock.Verify(hub => hub.PublishNotification(It.IsAny()), Times.Once()); } [Fact(DisplayName = "Notifications should be published when the publisher is running")] @@ -36,10 +45,17 @@ public async Task PublishNotification_WhenRestarted_PublishesNotification() // Arrange var wasCalled = new TaskCompletionSource(); var eventHubMock = new Mock(); + var serviceCollectionMock = new Mock(); + + var workflowOptions = new WorkflowOptions(serviceCollectionMock.Object) + { + EnableLifeCycleEventsPublisher = true + }; + eventHubMock .Setup(hub => hub.PublishNotification(It.IsAny())) .Callback(() => wasCalled.SetResult(true)); - LifeCycleEventPublisher publisher = new LifeCycleEventPublisher(eventHubMock.Object, new LoggerFactory()); + LifeCycleEventPublisher publisher = new LifeCycleEventPublisher(eventHubMock.Object, workflowOptions, new LoggerFactory()); // Act publisher.Start(); @@ -49,6 +65,33 @@ public async Task PublishNotification_WhenRestarted_PublishesNotification() // Assert await wasCalled.Task; + eventHubMock.Verify(hub => hub.PublishNotification(It.IsAny()), Times.Once()); + } + + [Fact(DisplayName = "Notifications should be disabled if option EnableLifeCycleEventsPublisher is disabled")] + public void PublishNotification_Disabled() + { + // Arrange + var eventHubMock = new Mock(); + var serviceCollectionMock = new Mock(); + + var workflowOptions = new WorkflowOptions(serviceCollectionMock.Object) + { + EnableLifeCycleEventsPublisher = false + }; + + eventHubMock + .Setup(hub => hub.PublishNotification(It.IsAny())) + .Returns(Task.CompletedTask); + LifeCycleEventPublisher publisher = new LifeCycleEventPublisher(eventHubMock.Object, workflowOptions, new LoggerFactory()); + + // Act + publisher.Start(); + publisher.PublishNotification(new StepCompleted()); + publisher.Stop(); + + // Assert + eventHubMock.Verify(hub => hub.PublishNotification(It.IsAny()), Times.Never()); } } } \ No newline at end of file From a6c84081ba577839cc17965246de1fc87458a11d Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 17 Aug 2021 07:25:24 -0700 Subject: [PATCH 365/462] Update Directory.Build.props --- src/Directory.Build.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index fa0f334be..13d001297 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -5,9 +5,9 @@ git https://github.com/danielgerlag/workflow-core.git 3.5.3 - 3.5.3.0 - 3.5.3.0 + 3.5.4.0 + 3.5.4.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.5.3 + 3.5.4 From 0ba1b75d934b8b8e0e53599b6ff66e5031205ba4 Mon Sep 17 00:00:00 2001 From: Uladzimir Kavaliuk Date: Tue, 17 Aug 2021 19:05:29 +0300 Subject: [PATCH 366/462] Fix issue 894 Added check whether collection is empty. Added specific test scenario. --- src/WorkflowCore/Primitives/Foreach.cs | 7 ++++- .../Scenarios/ForeachScenario.cs | 29 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/WorkflowCore/Primitives/Foreach.cs b/src/WorkflowCore/Primitives/Foreach.cs index 1a64ddfdb..ef4c39fe0 100644 --- a/src/WorkflowCore/Primitives/Foreach.cs +++ b/src/WorkflowCore/Primitives/Foreach.cs @@ -15,7 +15,12 @@ public override ExecutionResult Run(IStepExecutionContext context) { if (context.PersistenceData == null) { - var values = Collection.Cast(); + var values = Collection.Cast().ToList(); + if (!values.Any()) + { + return ExecutionResult.Next(); + } + if (RunParallel) { return ExecutionResult.Branch(new List(values), new IteratorPersistenceData { ChildrenActive = true }); diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs index fb0747d5e..774064b8b 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachScenario.cs @@ -18,7 +18,6 @@ public class ForeachScenario : WorkflowTest Numbers { get; set; } = new List(); + + public bool IsParallel { get; set; } = true; } public class ForeachWorkflow : IWorkflow @@ -43,7 +45,7 @@ public void Build(IWorkflowBuilder builder) Step1Ticker++; return ExecutionResult.Next(); }) - .ForEach(x => new List { 2, 2, 3 }) + .ForEach(x => x.Numbers, x => x.IsParallel) .Do(x => x.StartWith()) .Then(context => { @@ -57,12 +59,18 @@ public void Build(IWorkflowBuilder builder) public ForeachScenario() { Setup(); + + Step1Ticker = 0; + Step2Ticker = 0; + Step3Ticker = 0; + AfterLoopValue = 0; + CheckSum = 0; } [Fact] public void Scenario() { - var workflowId = StartWorkflow(null); + var workflowId = StartWorkflow(new MyDataClass { Numbers = new List { 2, 2, 3 } }); WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); Step1Ticker.Should().Be(1); @@ -73,5 +81,20 @@ public void Scenario() GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); UnhandledStepErrors.Count.Should().Be(0); } + + [Fact] + public void EmptyCollectionSequentialScenario() + { + var workflowId = StartWorkflow(new MyDataClass { IsParallel = false }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + Step1Ticker.Should().Be(1); + Step2Ticker.Should().Be(0); + Step3Ticker.Should().Be(1); + AfterLoopValue.Should().Be(0); + CheckSum.Should().Be(0); + } } } From 1efecabf70e36cd6fe6654f8aacc0eb431b3dc76 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 23 Aug 2021 15:00:26 +0200 Subject: [PATCH 367/462] Cleanup --- src/WorkflowCore/Interface/Persistence/ITransaction.cs | 10 ---------- src/WorkflowCore/Services/WorkflowController.cs | 4 ++-- .../Services/MongoPersistenceProvider.cs | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 src/WorkflowCore/Interface/Persistence/ITransaction.cs diff --git a/src/WorkflowCore/Interface/Persistence/ITransaction.cs b/src/WorkflowCore/Interface/Persistence/ITransaction.cs deleted file mode 100644 index cbb51e778..000000000 --- a/src/WorkflowCore/Interface/Persistence/ITransaction.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace WorkflowCore.Interface -{ - public interface ITransaction - { - /// - /// The transaction session specific for each storage provider. - /// - object Session { get; } - } -} \ No newline at end of file diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 1fb97f821..6edb63aa7 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -53,7 +53,7 @@ public Task StartWorkflow(string workflowId, TData data = null, s return StartWorkflow(workflowId, null, data, reference); } - public async Task StartWorkflow(string workflowId, int? version, TData data = null, string reference=null, ITransaction transaction = null) + public async Task StartWorkflow(string workflowId, int? version, TData data = null, string reference=null) where TData : class, new() { @@ -91,7 +91,7 @@ public async Task StartWorkflow(string workflowId, int? version, await middlewareRunner.RunPreMiddleware(wf, def); } - string id = await _persistenceStore.CreateNewWorkflow(wf, transaction); + string id = await _persistenceStore.CreateNewWorkflow(wf); await _queueProvider.QueueWork(id, QueueType.Workflow); await _queueProvider.QueueWork(id, QueueType.Index); await _eventHub.PublishNotification(new WorkflowStarted diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index fe4f88cb5..79e9fe5b7 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -126,7 +126,6 @@ static void CreateIndexes(MongoPersistenceProvider instance) public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { await WorkflowInstances.InsertOneAsync(workflow, cancellationToken: cancellationToken); - return workflow.Id; } From 66fc797496574c1ca0f4562951cc34dc859b9097 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 23 Aug 2021 15:08:52 +0200 Subject: [PATCH 368/462] Add transaction scope support --- .../Services/MongoPersistenceProvider.cs | 9 +++++---- .../WorkflowCore.Persistence.MongoDB.csproj | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 79e9fe5b7..fd6d00a44 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -12,6 +12,7 @@ using WorkflowCore.Interface; using WorkflowCore.Models; using System.Threading; +using MongoDB.Extensions.Transactions; namespace WorkflowCore.Persistence.MongoDB.Services { @@ -115,13 +116,13 @@ static void CreateIndexes(MongoPersistenceProvider instance) } } - private IMongoCollection WorkflowInstances => _database.GetCollection(WorkflowCollectionName); + private IMongoCollection WorkflowInstances => _database.GetCollection(WorkflowCollectionName).AsTransactionCollection(); - private IMongoCollection EventSubscriptions => _database.GetCollection("wfc.subscriptions"); + private IMongoCollection EventSubscriptions => _database.GetCollection("wfc.subscriptions").AsTransactionCollection(); - private IMongoCollection Events => _database.GetCollection("wfc.events"); + private IMongoCollection Events => _database.GetCollection("wfc.events").AsTransactionCollection(); - private IMongoCollection ExecutionErrors => _database.GetCollection("wfc.execution_errors"); + private IMongoCollection ExecutionErrors => _database.GetCollection("wfc.execution_errors").AsTransactionCollection(); public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 13df8d4aa..20c32aaaf 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -22,7 +22,8 @@ - + + From 2020d305c03aa5775d4616012485f065f19f4347 Mon Sep 17 00:00:00 2001 From: john Date: Wed, 1 Sep 2021 09:09:23 +0300 Subject: [PATCH 369/462] Resolve #403, unnecessary log info --- src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs index 9d5edc575..bf3d97ba9 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs @@ -55,7 +55,7 @@ private async void PollRunnables(object target) { try { - _logger.LogInformation("Polling for runnable workflows"); + _logger.LogDebug("Polling for runnable workflows"); var runnables = await _persistenceStore.GetRunnableInstances(_dateTimeProvider.Now); foreach (var item in runnables) { @@ -86,7 +86,7 @@ private async void PollRunnables(object target) { try { - _logger.LogInformation("Polling for unprocessed events"); + _logger.LogDebug("Polling for unprocessed events"); var events = await _persistenceStore.GetRunnableEvents(_dateTimeProvider.Now); foreach (var item in events.ToList()) { From 7425294683eb64c2651836e7ea5fac3d83dd9401 Mon Sep 17 00:00:00 2001 From: John Demetriou Date: Thu, 2 Sep 2021 11:36:05 +0300 Subject: [PATCH 370/462] Delete ServiceCollectionExtensions.cs Empty class. Re-introduce if ever needed --- .../WorkflowCore.WebAPI/ServiceCollectionExtensions.cs | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/extensions/WorkflowCore.WebAPI/ServiceCollectionExtensions.cs diff --git a/src/extensions/WorkflowCore.WebAPI/ServiceCollectionExtensions.cs b/src/extensions/WorkflowCore.WebAPI/ServiceCollectionExtensions.cs deleted file mode 100644 index 3a2ead3db..000000000 --- a/src/extensions/WorkflowCore.WebAPI/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Linq; - -namespace Microsoft.Extensions.DependencyInjection -{ - public static class ServiceCollectionExtensions - { - - } -} From b68bb062d7f20f78bd71a69048f045446c6fc466 Mon Sep 17 00:00:00 2001 From: glucaci Date: Fri, 3 Sep 2021 11:49:14 +0200 Subject: [PATCH 371/462] Revert and provide a new extension for creating db from outside --- .../ServiceCollectionExtensions.cs | 22 ++++++++++++++++++- .../Services/MongoPersistenceProvider.cs | 9 ++++---- .../WorkflowCore.Persistence.MongoDB.csproj | 3 +-- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs index 87b0e689e..9534fe0b2 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/ServiceCollectionExtensions.cs @@ -1,6 +1,5 @@ using MongoDB.Driver; using System; -using System.Linq; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Persistence.MongoDB.Services; @@ -33,5 +32,26 @@ public static WorkflowOptions UseMongoDB( }); return options; } + + public static WorkflowOptions UseMongoDB( + this WorkflowOptions options, + Func createDatabase) + { + if (options == null) throw new ArgumentNullException(nameof(options)); + if (createDatabase == null) throw new ArgumentNullException(nameof(createDatabase)); + + options.UsePersistence(sp => + { + var db = createDatabase(sp); + return new MongoPersistenceProvider(db); + }); + options.Services.AddTransient(sp => + { + var db = createDatabase(sp); + return new WorkflowPurger(db); + }); + + return options; + } } } diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index fd6d00a44..79e9fe5b7 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -12,7 +12,6 @@ using WorkflowCore.Interface; using WorkflowCore.Models; using System.Threading; -using MongoDB.Extensions.Transactions; namespace WorkflowCore.Persistence.MongoDB.Services { @@ -116,13 +115,13 @@ static void CreateIndexes(MongoPersistenceProvider instance) } } - private IMongoCollection WorkflowInstances => _database.GetCollection(WorkflowCollectionName).AsTransactionCollection(); + private IMongoCollection WorkflowInstances => _database.GetCollection(WorkflowCollectionName); - private IMongoCollection EventSubscriptions => _database.GetCollection("wfc.subscriptions").AsTransactionCollection(); + private IMongoCollection EventSubscriptions => _database.GetCollection("wfc.subscriptions"); - private IMongoCollection Events => _database.GetCollection("wfc.events").AsTransactionCollection(); + private IMongoCollection Events => _database.GetCollection("wfc.events"); - private IMongoCollection ExecutionErrors => _database.GetCollection("wfc.execution_errors").AsTransactionCollection(); + private IMongoCollection ExecutionErrors => _database.GetCollection("wfc.execution_errors"); public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 20c32aaaf..13df8d4aa 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -22,8 +22,7 @@ - - + From d6ab06e56b6d4b62e6a26deb85232974dc0de8f5 Mon Sep 17 00:00:00 2001 From: Mohamed Rslan Date: Sat, 4 Sep 2021 19:08:45 +0300 Subject: [PATCH 372/462] Upgrade NEST version to support elastic 7 fix startup exception "ArgumentException: Requested value 'data_cold' was not found" which the NEST V7.5.1 library doesn't have all new elastic roles enum mappings --- .../WorkflowCore.Providers.Elasticsearch.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj index db78bebb7..7bf5600b8 100644 --- a/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj +++ b/src/providers/WorkflowCore.Providers.Elasticsearch/WorkflowCore.Providers.Elasticsearch.csproj @@ -13,7 +13,7 @@ - + From ea6f184c47b5ddc3e9914b0d829590d195ceac83 Mon Sep 17 00:00:00 2001 From: Mohamed Rslan Date: Sat, 4 Sep 2021 19:24:16 +0300 Subject: [PATCH 373/462] Upgrade NEST Test version to support elastic 7 --- .../WorkflowCore.Tests.Elasticsearch.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 0ed01e311..60e631bd7 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -8,7 +8,7 @@ - + From 1c199e4a7a4fa89182843abfc8176fb0d6762114 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 11 Sep 2021 19:53:07 -0700 Subject: [PATCH 374/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 13d001297..cca812e1d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.5.3 - 3.5.4.0 - 3.5.4.0 + 3.5.5 + 3.5.5.0 + 3.5.5.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.5.4 + 3.5.5 From 2216ad4f10ac3155e1a4912c0fa6294b22831381 Mon Sep 17 00:00:00 2001 From: Sergii Kram Date: Tue, 14 Sep 2021 11:32:30 +0300 Subject: [PATCH 375/462] Fix SQL Server tests The repository 'microsoft/mssql-server-linux' is no longer maintained and container image cannot be pulled. 'mcr.microsoft.com/mssql/server' should be used instead. See https://cloudblogs.microsoft.com/sqlserver/2021/05/20/microsoft-sql-server-linux-based-container-images-to-be-available-only-from-the-microsoft-container-registry/ for details. --- test/WorkflowCore.Tests.SqlServer/DockerSetup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs b/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs index 74aff9487..b9d15ddff 100644 --- a/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs +++ b/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs @@ -11,7 +11,7 @@ public class SqlDockerSetup : DockerSetup public static string ConnectionString { get; set; } public static string ScenarioConnectionString { get; set; } - public override string ImageName => "microsoft/mssql-server-linux"; + public override string ImageName => "mcr.microsoft.com/mssql/server"; public override int InternalPort => 1433; public override TimeSpan TimeOut => TimeSpan.FromSeconds(120); From 2f9908b78977cb6ce0fec20934c638a875b5d19e Mon Sep 17 00:00:00 2001 From: Sergii Kram Date: Tue, 14 Sep 2021 12:25:55 +0300 Subject: [PATCH 376/462] Fix DynamoDB tests The latest 'amazon/dynamodb-local' image introduces breaking change and requires dummy credentials, see https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html. Without the fix tests fail with the timeout error in DockerSetup.StartContainer method but the real error is "Unable to get IAM security credentials from EC2 Instance Metadata Service." in the DynamoDbDockerSetup.TestReady method. --- test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs b/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs index b9458fd1e..83501a6d5 100644 --- a/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs +++ b/test/WorkflowCore.Tests.DynamoDB/DynamoDbDockerSetup.cs @@ -11,7 +11,7 @@ public class DynamoDbDockerSetup : DockerSetup { public static string ConnectionString { get; set; } - public static AWSCredentials Credentials => new EnvironmentVariablesAWSCredentials(); + public static AWSCredentials Credentials => new BasicAWSCredentials("DUMMYIDEXAMPLE", "DUMMYEXAMPLEKEY"); public override string ImageName => @"amazon/dynamodb-local"; public override int InternalPort => 8000; @@ -30,7 +30,7 @@ public override bool TestReady() { ServiceURL = $"http://localhost:{ExternalPort}" }; - AmazonDynamoDBClient client = new AmazonDynamoDBClient(clientConfig); + AmazonDynamoDBClient client = new AmazonDynamoDBClient(Credentials, clientConfig); var resp = client.ListTablesAsync().Result; return resp.HttpStatusCode == HttpStatusCode.OK; From f2ac1255e318b1b6c1cea216655de258ed05c7a7 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 19 Sep 2021 09:15:13 -0700 Subject: [PATCH 377/462] Update Directory.Build.props --- src/Directory.Build.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index cca812e1d..26c64f3ff 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -5,9 +5,9 @@ git https://github.com/danielgerlag/workflow-core.git 3.5.5 - 3.5.5.0 - 3.5.5.0 + 3.5.6.0 + 3.5.6.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.5.5 + 3.5.6 From beadef890d747be190d1adc27ce0773109be324f Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 8 Oct 2021 00:48:53 +0300 Subject: [PATCH 378/462] Fix issue #926 --- src/WorkflowCore/Services/SyncWorkflowRunner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WorkflowCore/Services/SyncWorkflowRunner.cs b/src/WorkflowCore/Services/SyncWorkflowRunner.cs index d0e0dcd89..5317a8966 100644 --- a/src/WorkflowCore/Services/SyncWorkflowRunner.cs +++ b/src/WorkflowCore/Services/SyncWorkflowRunner.cs @@ -87,7 +87,7 @@ public async Task RunWorkflowSync(string workflowId, in { while ((wf.Status == WorkflowStatus.Runnable) && !token.IsCancellationRequested) { - await _executor.Execute(wf); + await _executor.Execute(wf, token); if (persistSate) await _persistenceStore.PersistWorkflow(wf, token); } From 2452d73a54e0a8f0fb3bad30e0de822eda5dfe44 Mon Sep 17 00:00:00 2001 From: MitchellVermaning Date: Wed, 13 Oct 2021 10:48:37 +0200 Subject: [PATCH 379/462] Update of "System.Linq.Dynamic.Core" package **Describe the bug** Hello! Thank you for sharing this powerful library. Last week I have reported a bug in the Linq.Dynamic.Core package (https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/545). The `DynamicExpressionParser.ParseLambda()` function was unable to escape backslashes correctly. Can you please update this package to the newest version v1.2.13. Thank you! --- src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index 230286071..1a0130fc9 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -11,7 +11,7 @@ - + From 5fbaa59f9ed71461b7800d760178ec658b1b7f53 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 14 Oct 2021 09:51:59 -0700 Subject: [PATCH 380/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 26c64f3ff..e276b701b 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.5.5 - 3.5.6.0 - 3.5.6.0 + 3.5.7 + 3.5.7.0 + 3.5.7.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.5.6 + 3.5.7 From 583a2503cf5de5c88e12f2938f5d017e6f04bcd3 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 15 Oct 2021 12:28:10 -0700 Subject: [PATCH 381/462] scheduled commands --- .../Persistence/IPersistenceProvider.cs | 2 +- .../IScheduledCommandRepository.cs | 18 +++++ src/WorkflowCore/Models/ScheduledCommand.cs | 16 +++++ .../BackgroundTasks/RunnablePoller.cs | 69 ++++++++++++++++++- .../BackgroundTasks/WorkflowConsumer.cs | 22 ++++-- .../MemoryPersistenceProvider.cs | 12 ++++ .../TransientMemoryPersistenceProvider.cs | 12 ++++ src/WorkflowCore/WorkflowCore.csproj | 1 + .../EntityFrameworkPersistenceProvider.cs | 12 ++++ .../Services/MongoPersistenceProvider.cs | 51 ++++++++++++++ .../Services/RavendbPersistenceProvider.cs | 15 +++- .../Services/DynamoPersistenceProvider.cs | 12 ++++ .../Services/CosmosDbPersistenceProvider.cs | 12 ++++ .../Services/RedisPersistenceProvider.cs | 12 ++++ .../Scenarios/DelayScenario.cs | 57 +++++++++++++++ test/WorkflowCore.Testing/WorkflowTest.cs | 2 +- .../Scenarios/MongoDelayScenario.cs | 26 +++++++ .../Scenarios/PostgresDelayScenario.cs | 20 ++++++ .../Scenarios/SqlServerDelayScenario.cs | 20 ++++++ 19 files changed, 381 insertions(+), 10 deletions(-) create mode 100644 src/WorkflowCore/Interface/Persistence/IScheduledCommandRepository.cs create mode 100644 src/WorkflowCore/Models/ScheduledCommand.cs create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/DelayScenario.cs create mode 100644 test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDelayScenario.cs create mode 100644 test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDelayScenario.cs create mode 100644 test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDelayScenario.cs diff --git a/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs b/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs index 7bff56345..4b83f5920 100644 --- a/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs +++ b/src/WorkflowCore/Interface/Persistence/IPersistenceProvider.cs @@ -6,7 +6,7 @@ namespace WorkflowCore.Interface { - public interface IPersistenceProvider : IWorkflowRepository, ISubscriptionRepository, IEventRepository + public interface IPersistenceProvider : IWorkflowRepository, ISubscriptionRepository, IEventRepository, IScheduledCommandRepository { Task PersistErrors(IEnumerable errors, CancellationToken cancellationToken = default); diff --git a/src/WorkflowCore/Interface/Persistence/IScheduledCommandRepository.cs b/src/WorkflowCore/Interface/Persistence/IScheduledCommandRepository.cs new file mode 100644 index 000000000..a86bccc42 --- /dev/null +++ b/src/WorkflowCore/Interface/Persistence/IScheduledCommandRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using WorkflowCore.Models; + +namespace WorkflowCore.Interface +{ + public interface IScheduledCommandRepository + { + bool SupportsScheduledCommands { get; } + + Task ScheduleCommand(ScheduledCommand command); + + Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default); + } +} diff --git a/src/WorkflowCore/Models/ScheduledCommand.cs b/src/WorkflowCore/Models/ScheduledCommand.cs new file mode 100644 index 000000000..de3dcc5dd --- /dev/null +++ b/src/WorkflowCore/Models/ScheduledCommand.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace WorkflowCore.Models +{ + public class ScheduledCommand + { + public const string ProcessWorkflow = "ProcessWorkflow"; + public const string ProcessEvent = "ProcessEvent"; + + public string CommandName { get; set; } + public string Data { get; set; } + public long ExecuteTime { get; set; } + } +} diff --git a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs index 9d5edc575..4e7101d57 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -48,6 +49,13 @@ public void Stop() /// Poll the persistence store for stashed unpublished events /// private async void PollRunnables(object target) + { + await PollWorkflows(); + await PollEvents(); + await PollCommands(); + } + + private async Task PollWorkflows() { try { @@ -55,10 +63,20 @@ private async void PollRunnables(object target) { try { - _logger.LogInformation("Polling for runnable workflows"); + _logger.LogInformation("Polling for runnable workflows"); var runnables = await _persistenceStore.GetRunnableInstances(_dateTimeProvider.Now); foreach (var item in runnables) { + if (_persistenceStore.SupportsScheduledCommands) + { + await _persistenceStore.ScheduleCommand(new ScheduledCommand() + { + CommandName = ScheduledCommand.ProcessWorkflow, + Data = item, + ExecuteTime = _dateTimeProvider.UtcNow.Ticks + }); + continue; + } if (_greylist.Contains($"wf:{item}")) { _logger.LogDebug($"Got greylisted workflow {item}"); @@ -79,17 +97,30 @@ private async void PollRunnables(object target) { _logger.LogError(ex, ex.Message); } + } + private async Task PollEvents() + { try { if (await _lockProvider.AcquireLock("unprocessed events", new CancellationToken())) { try { - _logger.LogInformation("Polling for unprocessed events"); + _logger.LogInformation("Polling for unprocessed events"); var events = await _persistenceStore.GetRunnableEvents(_dateTimeProvider.Now); foreach (var item in events.ToList()) { + if (_persistenceStore.SupportsScheduledCommands) + { + await _persistenceStore.ScheduleCommand(new ScheduledCommand() + { + CommandName = ScheduledCommand.ProcessEvent, + Data = item, + ExecuteTime = _dateTimeProvider.UtcNow.Ticks + }); + continue; + } if (_greylist.Contains($"evt:{item}")) { _logger.LogDebug($"Got greylisted event {item}"); @@ -111,5 +142,39 @@ private async void PollRunnables(object target) _logger.LogError(ex, ex.Message); } } + + private async Task PollCommands() + { + try + { + if (await _lockProvider.AcquireLock("poll-commands", new CancellationToken())) + { + try + { + _logger.LogInformation("Polling for scheduled commands"); + await _persistenceStore.ProcessCommands(new DateTimeOffset(_dateTimeProvider.UtcNow), async (command) => + { + switch (command.CommandName) + { + case ScheduledCommand.ProcessWorkflow: + await _queueProvider.QueueWork(command.Data, QueueType.Workflow); + break; + case ScheduledCommand.ProcessEvent: + await _queueProvider.QueueWork(command.Data, QueueType.Event); + break; + } + }); + } + finally + { + await _lockProvider.ReleaseLock("poll-commands"); + } + } + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + } + } } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 6e5c1d3c1..a387848b6 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -69,11 +69,25 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance await _persistenceStore.PersistErrors(result.Errors, cancellationToken); - var readAheadTicks = _datetimeProvider.UtcNow.Add(Options.PollInterval).Ticks; - - if ((workflow.Status == WorkflowStatus.Runnable) && workflow.NextExecution.HasValue && workflow.NextExecution.Value < readAheadTicks) + if ((workflow.Status == WorkflowStatus.Runnable) && workflow.NextExecution.HasValue) { - new Task(() => FutureQueue(workflow, cancellationToken)).Start(); + var readAheadTicks = _datetimeProvider.UtcNow.Add(Options.PollInterval).Ticks; + if (workflow.NextExecution.Value < readAheadTicks) + { + new Task(() => FutureQueue(workflow, cancellationToken)).Start(); + } + else + { + if (_persistenceStore.SupportsScheduledCommands) + { + await _persistenceStore.ScheduleCommand(new ScheduledCommand() + { + CommandName = ScheduledCommand.ProcessWorkflow, + Data = workflow.Id, + ExecuteTime = workflow.NextExecution.Value + }); + } + } } } } diff --git a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs index 96ea772af..e983f8fd5 100644 --- a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs @@ -24,6 +24,8 @@ public class MemoryPersistenceProvider : ISingletonMemoryProvider private readonly List _events = new List(); private readonly List _errors = new List(); + public bool SupportsScheduledCommands => false; + public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken _ = default) { lock (_instances) @@ -255,6 +257,16 @@ public async Task PersistErrors(IEnumerable errors, Cancellation _errors.AddRange(errors); } } + + public Task ScheduleCommand(ScheduledCommand command) + { + throw new NotImplementedException(); + } + + public Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously diff --git a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs index c2fab66e1..0a94c6f8e 100644 --- a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs @@ -11,6 +11,8 @@ public class TransientMemoryPersistenceProvider : IPersistenceProvider { private readonly ISingletonMemoryProvider _innerService; + public bool SupportsScheduledCommands => false; + public TransientMemoryPersistenceProvider(ISingletonMemoryProvider innerService) { _innerService = innerService; @@ -56,5 +58,15 @@ public TransientMemoryPersistenceProvider(ISingletonMemoryProvider innerService) public Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken _ = default) => _innerService.SetSubscriptionToken(eventSubscriptionId, token, workerId, expiry); public Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken _ = default) => _innerService.ClearSubscriptionToken(eventSubscriptionId, token); + + public Task ScheduleCommand(ScheduledCommand command) + { + throw new NotImplementedException(); + } + + public Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } } diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 1457092bd..800ef9c08 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -26,6 +26,7 @@ + diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs index e0c7082fd..40bd892e8 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs @@ -17,6 +17,8 @@ public class EntityFrameworkPersistenceProvider : IPersistenceProvider private readonly bool _canMigrateDB; private readonly IWorkflowDbContextFactory _contextFactory; + public bool SupportsScheduledCommands => false; + public EntityFrameworkPersistenceProvider(IWorkflowDbContextFactory contextFactory, bool canCreateDB, bool canMigrateDB) { _contextFactory = contextFactory; @@ -365,5 +367,15 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke await db.SaveChangesAsync(cancellationToken); } } + + public Task ScheduleCommand(ScheduledCommand command) + { + throw new NotImplementedException(); + } + + public Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } } diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 79e9fe5b7..ac65eb39b 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -83,6 +83,8 @@ static MongoPersistenceProvider() BsonClassMap.RegisterClassMap(x => x.AutoMap()); BsonClassMap.RegisterClassMap(x => x.AutoMap()); BsonClassMap.RegisterClassMap(x => x.AutoMap()); + BsonClassMap.RegisterClassMap(x => x.AutoMap()) + .SetIgnoreExtraElements(true); } static bool indexesCreated = false; @@ -111,6 +113,17 @@ static void CreateIndexes(MongoPersistenceProvider instance) .Ascending(x => x.EventKey), new CreateIndexOptions { Background = true, Name = "idx_namekey" })); + instance.ScheduledCommands.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys + .Descending(x => x.ExecuteTime), + new CreateIndexOptions { Background = true, Name = "idx_exectime" })); + + instance.ScheduledCommands.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys + .Ascending(x => x.CommandName) + .Ascending(x => x.Data), + new CreateIndexOptions { Background = true, Unique = true, Name = "idx_key" })); + indexesCreated = true; } } @@ -123,6 +136,8 @@ static void CreateIndexes(MongoPersistenceProvider instance) private IMongoCollection ExecutionErrors => _database.GetCollection("wfc.execution_errors"); + private IMongoCollection ScheduledCommands => _database.GetCollection("wfc.scheduled_commands"); + public async Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { await WorkflowInstances.InsertOneAsync(workflow, cancellationToken: cancellationToken); @@ -291,5 +306,41 @@ public async Task PersistErrors(IEnumerable errors, Cancellation if (errors.Any()) await ExecutionErrors.InsertManyAsync(errors, cancellationToken: cancellationToken); } + + public bool SupportsScheduledCommands => true; + + public async Task ScheduleCommand(ScheduledCommand command) + { + try + { + await ScheduledCommands.InsertOneAsync(command); + } + catch (MongoBulkWriteException ex) + { + if (ex.WriteErrors.All(x => x.Category == ServerErrorCategory.DuplicateKey)) + return; + throw; + } + } + + public async Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) + { + var cursor = await ScheduledCommands.FindAsync(x => x.ExecuteTime < asOf.UtcDateTime.Ticks); + while (await cursor.MoveNextAsync(cancellationToken)) + { + foreach (var command in cursor.Current) + { + try + { + await action(command); + await ScheduledCommands.DeleteOneAsync(x => x.CommandName == command.CommandName && x.Data == command.Data); + } + catch (Exception) + { + //TODO: add logger + } + } + } + } } } diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs index d115f1eb2..695b8e31f 100644 --- a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs @@ -18,7 +18,9 @@ public class RavendbPersistenceProvider : IPersistenceProvider private readonly IDocumentStore _database; static bool indexesCreated = false; - public RavendbPersistenceProvider(IDocumentStore database) + public bool SupportsScheduledCommands => false; + + public RavendbPersistenceProvider(IDocumentStore database) { _database = database; CreateIndexes(this); @@ -316,5 +318,14 @@ public async Task PersistErrors(IEnumerable errors, Cancellation } } - } + public Task ScheduleCommand(ScheduledCommand command) + { + throw new NotImplementedException(); + } + + public Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + } } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index d3828389a..1126b6c20 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -24,6 +24,8 @@ public class DynamoPersistenceProvider : IPersistenceProvider public const string SUBCRIPTION_TABLE = "subscriptions"; public const string EVENT_TABLE = "events"; + public bool SupportsScheduledCommands => false; + public DynamoPersistenceProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, IDynamoDbProvisioner provisioner, string tablePrefix, ILoggerFactory logFactory) { _logger = logFactory.CreateLogger(); @@ -469,5 +471,15 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke await _client.UpdateItemAsync(request, cancellationToken); } + + public Task ScheduleCommand(ScheduledCommand command) + { + throw new NotImplementedException(); + } + + public Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs index 36e51b86c..644009df2 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs @@ -35,6 +35,8 @@ public CosmosDbPersistenceProvider( _subscriptionContainer = new Lazy(() => _clientFactory.GetCosmosClient().GetDatabase(_dbId).GetContainer(cosmosDbStorageOptions.SubscriptionContainerName)); } + public bool SupportsScheduledCommands => false; + public async Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken cancellationToken = default) { var existing = await _subscriptionContainer.Value.ReadItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId)); @@ -225,6 +227,16 @@ public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken c await _workflowContainer.Value.UpsertItemAsync(PersistedWorkflow.FromInstance(workflow), cancellationToken: cancellationToken); } + public Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task ScheduleCommand(ScheduledCommand command) + { + throw new NotImplementedException(); + } + public async Task SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken cancellationToken) { var sub = await _subscriptionContainer.Value.ReadItemAsync(eventSubscriptionId, new PartitionKey(eventSubscriptionId), cancellationToken: cancellationToken); diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index dcd932af0..cde65e2e5 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -28,6 +28,8 @@ public class RedisPersistenceProvider : IPersistenceProvider private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; private readonly bool _removeComplete; + public bool SupportsScheduledCommands => false; + public RedisPersistenceProvider(string connectionString, string prefix, bool removeComplete, ILoggerFactory logFactory) { _connectionString = connectionString; @@ -232,5 +234,15 @@ public Task PersistErrors(IEnumerable errors, CancellationToken public void EnsureStoreExists() { } + + public Task ScheduleCommand(ScheduledCommand command) + { + throw new NotImplementedException(); + } + + public Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } } diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/DelayScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/DelayScenario.cs new file mode 100644 index 000000000..704b27d79 --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/DelayScenario.cs @@ -0,0 +1,57 @@ +using System; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class DelayWorkflow : IWorkflow + { + internal static int Step1Ticker = 0; + internal static int Step2Ticker = 0; + + public string Id => "DelayWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(context => Step1Ticker++) + .Delay(data => data.WaitTime) + .Then(context => Step2Ticker++); + + } + + public class MyDataClass + { + public TimeSpan WaitTime { get; set; } + } + } + + public class DelayScenario : WorkflowTest + { + public DelayScenario() + { + Setup(); + } + + [Fact] + public void Scenario() + { + DelayWorkflow.Step1Ticker = 0; + DelayWorkflow.Step2Ticker = 0; + + var workflowId = StartWorkflow(new DelayWorkflow.MyDataClass() + { + WaitTime = Host.Options.PollInterval.Add(TimeSpan.FromSeconds(1)) + }); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(0); + DelayWorkflow.Step1Ticker.Should().Be(1); + DelayWorkflow.Step2Ticker.Should().Be(1); + } + } +} diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/test/WorkflowCore.Testing/WorkflowTest.cs index 46a6d0ce8..bf0eb97ab 100644 --- a/test/WorkflowCore.Testing/WorkflowTest.cs +++ b/test/WorkflowCore.Testing/WorkflowTest.cs @@ -46,7 +46,7 @@ protected void Host_OnStepError(WorkflowInstance workflow, WorkflowStep step, Ex protected virtual void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(); + services.AddWorkflow(options => options.UsePollInterval(TimeSpan.FromSeconds(3))); } public string StartWorkflow(TData data) diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDelayScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDelayScenario.cs new file mode 100644 index 000000000..511aa79fb --- /dev/null +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDelayScenario.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson.Serialization; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.MongoDB.Scenarios +{ + [Collection("Mongo collection")] + public class MongoDelayScenario : DelayScenario + { + public MongoDelayScenario() : base() + { + BsonClassMap.RegisterClassMap(map => map.AutoMap()); + } + + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(cfg => + { + cfg.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests"); + cfg.UsePollInterval(TimeSpan.FromSeconds(2)); + }); + } + } +} diff --git a/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDelayScenario.cs b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDelayScenario.cs new file mode 100644 index 000000000..649632ac5 --- /dev/null +++ b/test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDelayScenario.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.PostgreSQL.Scenarios +{ + [Collection("Postgres collection")] + public class PostgresDelayScenario : DelayScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(cfg => + { + cfg.UsePostgreSQL(PostgresDockerSetup.ScenarioConnectionString, true, true); + cfg.UsePollInterval(TimeSpan.FromSeconds(2)); + }); + } + } +} diff --git a/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDelayScenario.cs b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDelayScenario.cs new file mode 100644 index 000000000..5e623c1db --- /dev/null +++ b/test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDelayScenario.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.SqlServer.Scenarios +{ + [Collection("SqlServer collection")] + public class SqlServerDelayScenario : DelayScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(cfg => + { + cfg.UseSqlServer(SqlDockerSetup.ScenarioConnectionString, true, true); + cfg.UsePollInterval(TimeSpan.FromSeconds(2)); + }); + } + } +} From b08f2c069dce0a22b7b08cad15569a05a571f4fc Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 15 Oct 2021 15:11:33 -0700 Subject: [PATCH 382/462] ef migrations --- src/Directory.Build.props | 8 +- .../BackgroundTasks/RunnablePoller.cs | 38 +- .../ExtensionMethods.cs | 20 + .../Models/PersistedScheduledCommand.cs | 20 + .../EntityFrameworkPersistenceProvider.cs | 41 +- .../Services/WorkflowDbContext.cs | 4 + ...lowCore.Persistence.EntityFramework.csproj | 2 +- ...11015215708_scheduled-commands.Designer.cs | 357 +++++++++++++++++ .../20211015215708_scheduled-commands.cs | 337 ++++++++++++++++ .../MysqlPersistenceProviderModelSnapshot.cs | 153 ++++--- .../WorkflowCore.Persistence.MySQL.csproj | 2 +- ...11015204734_scheduled-commands.Designer.cs | 366 +++++++++++++++++ .../20211015204734_scheduled-commands.cs | 43 ++ ...ostgresPersistenceProviderModelSnapshot.cs | 146 ++++--- ...11015215114_scheduled-commands.Designer.cs | 379 ++++++++++++++++++ .../20211015215114_scheduled-commands.cs | 43 ++ ...lServerPersistenceProviderModelSnapshot.cs | 60 ++- .../Scenarios/MysqlDelayScenario.cs | 20 + 18 files changed, 1902 insertions(+), 137 deletions(-) create mode 100644 src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedScheduledCommand.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.cs create mode 100644 src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.cs create mode 100644 src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.Designer.cs create mode 100644 src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.cs create mode 100644 test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDelayScenario.cs diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e276b701b..7c162693d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.5.7 - 3.5.7.0 - 3.5.7.0 + 3.6.0 + 3.6.0.0 + 3.6.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.5.7 + 3.6.0 diff --git a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs index 4e7101d57..b40a7a208 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs @@ -69,13 +69,20 @@ private async Task PollWorkflows() { if (_persistenceStore.SupportsScheduledCommands) { - await _persistenceStore.ScheduleCommand(new ScheduledCommand() + try { - CommandName = ScheduledCommand.ProcessWorkflow, - Data = item, - ExecuteTime = _dateTimeProvider.UtcNow.Ticks - }); - continue; + await _persistenceStore.ScheduleCommand(new ScheduledCommand() + { + CommandName = ScheduledCommand.ProcessWorkflow, + Data = item, + ExecuteTime = _dateTimeProvider.UtcNow.Ticks + }); + continue; + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + } } if (_greylist.Contains($"wf:{item}")) { @@ -113,13 +120,20 @@ private async Task PollEvents() { if (_persistenceStore.SupportsScheduledCommands) { - await _persistenceStore.ScheduleCommand(new ScheduledCommand() + try { - CommandName = ScheduledCommand.ProcessEvent, - Data = item, - ExecuteTime = _dateTimeProvider.UtcNow.Ticks - }); - continue; + await _persistenceStore.ScheduleCommand(new ScheduledCommand() + { + CommandName = ScheduledCommand.ProcessEvent, + Data = item, + ExecuteTime = _dateTimeProvider.UtcNow.Ticks + }); + continue; + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + } } if (_greylist.Contains($"evt:{item}")) { diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs index c978131d1..b0b2b1d99 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs @@ -123,6 +123,16 @@ internal static PersistedEvent ToPersistable(this Event instance) return result; } + internal static PersistedScheduledCommand ToPersistable(this ScheduledCommand instance) + { + var result = new PersistedScheduledCommand(); + result.CommandName = instance.CommandName; + result.Data = instance.Data; + result.ExecuteTime = instance.ExecuteTime; + + return result; + } + internal static WorkflowInstance ToWorkflowInstance(this PersistedWorkflow instance) { WorkflowInstance result = new WorkflowInstance(); @@ -219,5 +229,15 @@ internal static Event ToEvent(this PersistedEvent instance) return result; } + + internal static ScheduledCommand ToScheduledCommand(this PersistedScheduledCommand instance) + { + var result = new ScheduledCommand(); + result.CommandName = instance.CommandName; + result.Data = instance.Data; + result.ExecuteTime = instance.ExecuteTime; + + return result; + } } } diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedScheduledCommand.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedScheduledCommand.cs new file mode 100644 index 000000000..7dad156a3 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Models/PersistedScheduledCommand.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; + +namespace WorkflowCore.Persistence.EntityFramework.Models +{ + public class PersistedScheduledCommand + { + [Key] + public long PersistenceId { get; set; } + + [MaxLength(200)] + public string CommandName { get; set; } + + [MaxLength(500)] + public string Data { get; set; } + + public long ExecuteTime { get; set; } + } +} diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs index 40bd892e8..56dd7f90c 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs @@ -17,7 +17,7 @@ public class EntityFrameworkPersistenceProvider : IPersistenceProvider private readonly bool _canMigrateDB; private readonly IWorkflowDbContextFactory _contextFactory; - public bool SupportsScheduledCommands => false; + public bool SupportsScheduledCommands => true; public EntityFrameworkPersistenceProvider(IWorkflowDbContextFactory contextFactory, bool canCreateDB, bool canMigrateDB) { @@ -368,14 +368,45 @@ public async Task ClearSubscriptionToken(string eventSubscriptionId, string toke } } - public Task ScheduleCommand(ScheduledCommand command) + public async Task ScheduleCommand(ScheduledCommand command) { - throw new NotImplementedException(); + try + { + using (var db = ConstructDbContext()) + { + var persistable = command.ToPersistable(); + var result = db.Set().Add(persistable); + await db.SaveChangesAsync(); + } + } + catch (DbUpdateException) + { + //log + } } - public Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) + public async Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + using (var db = ConstructDbContext()) + { + var cursor = db.Set() + .Where(x => x.ExecuteTime < asOf.UtcDateTime.Ticks) + .AsAsyncEnumerable(); + + await foreach (var command in cursor) + { + try + { + await action(command.ToScheduledCommand()); + db.Set().Remove(command); + await db.SaveChangesAsync(); + } + catch (Exception) + { + //TODO: add logger + } + } + } } } } diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs index 7b80db3da..645a26c3d 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs @@ -38,6 +38,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) events.HasIndex(x => x.EventTime); events.HasIndex(x => x.IsProcessed); + var commands = modelBuilder.Entity(); + commands.HasIndex(x => x.ExecuteTime); + commands.HasIndex(x => new { x.CommandName, x.Data}).IsUnique(); + ConfigureWorkflowStorage(workflows); ConfigureExecutionPointerStorage(executionPointers); ConfigureExecutionErrorStorage(executionErrors); diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 39627c7c9..6e1206632 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -3,7 +3,7 @@ Workflow Core EntityFramework Core Persistence Provider Daniel Gerlag - netstandard2.0 + netstandard2.1 WorkflowCore.Persistence.EntityFramework WorkflowCore.Persistence.EntityFramework workflow;.NET;Core;state machine;WorkflowCore;EntityFramework;EntityFrameworkCore diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.Designer.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.Designer.cs new file mode 100644 index 000000000..9573baf3e --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.Designer.cs @@ -0,0 +1,357 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WorkflowCore.Persistence.MySQL; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + [DbContext(typeof(MysqlContext))] + [Migration("20211015215708_scheduled-commands")] + partial class scheduledcommands + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.8"); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("EventData") + .HasColumnType("longtext"); + + b.Property("EventId") + .HasColumnType("char(36)"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventTime") + .HasColumnType("datetime(6)"); + + b.Property("IsProcessed") + .HasColumnType("tinyint(1)"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ErrorTime") + .HasColumnType("datetime(6)"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("WorkflowId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Children") + .HasColumnType("longtext"); + + b.Property("ContextItem") + .HasColumnType("longtext"); + + b.Property("EndTime") + .HasColumnType("datetime(6)"); + + b.Property("EventData") + .HasColumnType("longtext"); + + b.Property("EventKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("EventName") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("EventPublished") + .HasColumnType("tinyint(1)"); + + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Outcome") + .HasColumnType("longtext"); + + b.Property("PersistenceData") + .HasColumnType("longtext"); + + b.Property("PredecessorId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Scope") + .HasColumnType("longtext"); + + b.Property("SleepUntil") + .HasColumnType("datetime(6)"); + + b.Property("StartTime") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("StepName") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("WorkflowId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AttributeKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("AttributeValue") + .HasColumnType("longtext"); + + b.Property("ExecutionPointerId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedScheduledCommand", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CommandName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Data") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ExecuteTime") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecuteTime"); + + b.HasIndex("CommandName", "Data") + .IsUnique(); + + b.ToTable("PersistedScheduledCommand"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExecutionPointerId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExternalToken") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExternalTokenExpiry") + .HasColumnType("datetime(6)"); + + b.Property("ExternalWorkerId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("SubscribeAsOf") + .HasColumnType("datetime(6)"); + + b.Property("SubscriptionData") + .HasColumnType("longtext"); + + b.Property("SubscriptionId") + .HasMaxLength(200) + .HasColumnType("char(200)"); + + b.Property("WorkflowId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CompleteTime") + .HasColumnType("datetime(6)"); + + b.Property("CreateTime") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("InstanceId") + .HasMaxLength(200) + .HasColumnType("char(200)"); + + b.Property("NextExecution") + .HasColumnType("bigint"); + + b.Property("Reference") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Version") + .HasColumnType("int"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Workflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Navigation("ExtensionAttributes"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Navigation("ExecutionPointers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.cs new file mode 100644 index 000000000..b08be15b8 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.cs @@ -0,0 +1,337 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.MySQL.Migrations +{ + public partial class scheduledcommands : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "InstanceId", + table: "Workflow", + type: "char(200)", + maxLength: 200, + nullable: false, + collation: "ascii_general_ci", + oldClrType: typeof(string), + oldType: "char(200)", + oldMaxLength: 200) + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Data", + table: "Workflow", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "SubscriptionId", + table: "Subscription", + type: "char(200)", + maxLength: 200, + nullable: false, + collation: "ascii_general_ci", + oldClrType: typeof(string), + oldType: "char(200)", + oldMaxLength: 200) + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "SubscriptionData", + table: "Subscription", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "AttributeValue", + table: "ExtensionAttribute", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Scope", + table: "ExecutionPointer", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "PersistenceData", + table: "ExecutionPointer", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Outcome", + table: "ExecutionPointer", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "EventData", + table: "ExecutionPointer", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "ContextItem", + table: "ExecutionPointer", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Children", + table: "ExecutionPointer", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Message", + table: "ExecutionError", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "EventData", + table: "Event", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "PersistedScheduledCommand", + columns: table => new + { + PersistenceId = table.Column(type: "bigint", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CommandName = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Data = table.Column(type: "varchar(500)", maxLength: 500, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ExecuteTime = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PersistedScheduledCommand", x => x.PersistenceId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_PersistedScheduledCommand_CommandName_Data", + table: "PersistedScheduledCommand", + columns: new[] { "CommandName", "Data" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_PersistedScheduledCommand_ExecuteTime", + table: "PersistedScheduledCommand", + column: "ExecuteTime"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "PersistedScheduledCommand"); + + migrationBuilder.AlterColumn( + name: "InstanceId", + table: "Workflow", + type: "char(200)", + maxLength: 200, + nullable: false, + oldClrType: typeof(Guid), + oldType: "char(200)", + oldMaxLength: 200) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("Relational:Collation", "ascii_general_ci"); + + migrationBuilder.AlterColumn( + name: "Data", + table: "Workflow", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "SubscriptionId", + table: "Subscription", + type: "char(200)", + maxLength: 200, + nullable: false, + oldClrType: typeof(Guid), + oldType: "char(200)", + oldMaxLength: 200) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("Relational:Collation", "ascii_general_ci"); + + migrationBuilder.AlterColumn( + name: "SubscriptionData", + table: "Subscription", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "AttributeValue", + table: "ExtensionAttribute", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Scope", + table: "ExecutionPointer", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "PersistenceData", + table: "ExecutionPointer", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Outcome", + table: "ExecutionPointer", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "EventData", + table: "ExecutionPointer", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "ContextItem", + table: "ExecutionPointer", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Children", + table: "ExecutionPointer", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Message", + table: "ExecutionError", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "EventData", + table: "Event", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs index c72c16199..cd2b9ceeb 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs @@ -2,6 +2,8 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WorkflowCore.Persistence.MySQL; namespace WorkflowCore.Persistence.MySQL.Migrations { @@ -12,8 +14,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.2") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.8"); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => { @@ -22,18 +24,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bigint"); b.Property("EventData") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("EventId") .HasColumnType("char(36)"); b.Property("EventKey") - .HasColumnType("varchar(200) CHARACTER SET utf8mb4") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("varchar(200)"); b.Property("EventName") - .HasColumnType("varchar(200) CHARACTER SET utf8mb4") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("varchar(200)"); b.Property("EventTime") .HasColumnType("datetime(6)"); @@ -65,15 +67,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime(6)"); b.Property("ExecutionPointerId") - .HasColumnType("varchar(100) CHARACTER SET utf8mb4") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("varchar(100)"); b.Property("Message") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("WorkflowId") - .HasColumnType("varchar(100) CHARACTER SET utf8mb4") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("varchar(100)"); b.HasKey("PersistenceId"); @@ -90,47 +92,47 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("tinyint(1)"); b.Property("Children") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("ContextItem") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("EndTime") .HasColumnType("datetime(6)"); b.Property("EventData") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("EventKey") - .HasColumnType("varchar(100) CHARACTER SET utf8mb4") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("varchar(100)"); b.Property("EventName") - .HasColumnType("varchar(100) CHARACTER SET utf8mb4") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("varchar(100)"); b.Property("EventPublished") .HasColumnType("tinyint(1)"); b.Property("Id") - .HasColumnType("varchar(50) CHARACTER SET utf8mb4") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("varchar(50)"); b.Property("Outcome") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("PersistenceData") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("PredecessorId") - .HasColumnType("varchar(100) CHARACTER SET utf8mb4") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("varchar(100)"); b.Property("RetryCount") .HasColumnType("int"); b.Property("Scope") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("SleepUntil") .HasColumnType("datetime(6)"); @@ -145,8 +147,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("int"); b.Property("StepName") - .HasColumnType("varchar(100) CHARACTER SET utf8mb4") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("varchar(100)"); b.Property("WorkflowId") .HasColumnType("bigint"); @@ -165,11 +167,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bigint"); b.Property("AttributeKey") - .HasColumnType("varchar(100) CHARACTER SET utf8mb4") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("varchar(100)"); b.Property("AttributeValue") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("ExecutionPointerId") .HasColumnType("bigint"); @@ -181,6 +183,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("ExtensionAttribute"); }); + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedScheduledCommand", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CommandName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Data") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ExecuteTime") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecuteTime"); + + b.HasIndex("CommandName", "Data") + .IsUnique(); + + b.ToTable("PersistedScheduledCommand"); + }); + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => { b.Property("PersistenceId") @@ -188,27 +217,27 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bigint"); b.Property("EventKey") - .HasColumnType("varchar(200) CHARACTER SET utf8mb4") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("varchar(200)"); b.Property("EventName") - .HasColumnType("varchar(200) CHARACTER SET utf8mb4") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("varchar(200)"); b.Property("ExecutionPointerId") - .HasColumnType("varchar(200) CHARACTER SET utf8mb4") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("varchar(200)"); b.Property("ExternalToken") - .HasColumnType("varchar(200) CHARACTER SET utf8mb4") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("varchar(200)"); b.Property("ExternalTokenExpiry") .HasColumnType("datetime(6)"); b.Property("ExternalWorkerId") - .HasColumnType("varchar(200) CHARACTER SET utf8mb4") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("varchar(200)"); b.Property("StepId") .HasColumnType("int"); @@ -217,15 +246,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime(6)"); b.Property("SubscriptionData") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("SubscriptionId") - .HasColumnType("char(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("char(200)"); b.Property("WorkflowId") - .HasColumnType("varchar(200) CHARACTER SET utf8mb4") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("varchar(200)"); b.HasKey("PersistenceId"); @@ -252,22 +281,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime(6)"); b.Property("Data") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Description") - .HasColumnType("varchar(500) CHARACTER SET utf8mb4") - .HasMaxLength(500); + .HasMaxLength(500) + .HasColumnType("varchar(500)"); b.Property("InstanceId") - .HasColumnType("char(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("char(200)"); b.Property("NextExecution") .HasColumnType("bigint"); b.Property("Reference") - .HasColumnType("varchar(200) CHARACTER SET utf8mb4") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("varchar(200)"); b.Property("Status") .HasColumnType("int"); @@ -276,8 +305,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("int"); b.Property("WorkflowDefinitionId") - .HasColumnType("varchar(200) CHARACTER SET utf8mb4") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("varchar(200)"); b.HasKey("PersistenceId"); @@ -296,6 +325,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("WorkflowId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("Workflow"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => @@ -305,6 +336,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("ExecutionPointerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Navigation("ExtensionAttributes"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Navigation("ExecutionPointers"); }); #pragma warning restore 612, 618 } diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index e0e83315b..1b2fa9f6b 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -4,7 +4,7 @@ Workflow Core MySQL Persistence Provider 1.0.0 Daniel Gerlag - netstandard2.0;netstandard2.1 + netstandard2.1 WorkflowCore.Persistence.MySQL WorkflowCore.Persistence.MySQL workflow;.NET;Core;state machine;WorkflowCore;MySQL diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.Designer.cs new file mode 100644 index 000000000..caa2f0941 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.Designer.cs @@ -0,0 +1,366 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using WorkflowCore.Persistence.PostgreSQL; + +namespace WorkflowCore.Persistence.PostgreSQL.Migrations +{ + [DbContext(typeof(PostgresContext))] + [Migration("20211015204734_scheduled-commands")] + partial class scheduledcommands + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.8") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("EventData") + .HasColumnType("text"); + + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EventTime") + .HasColumnType("timestamp without time zone"); + + b.Property("IsProcessed") + .HasColumnType("boolean"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ErrorTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("WorkflowId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Children") + .HasColumnType("text"); + + b.Property("ContextItem") + .HasColumnType("text"); + + b.Property("EndTime") + .HasColumnType("timestamp without time zone"); + + b.Property("EventData") + .HasColumnType("text"); + + b.Property("EventKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("EventName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("EventPublished") + .HasColumnType("boolean"); + + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Outcome") + .HasColumnType("text"); + + b.Property("PersistenceData") + .HasColumnType("text"); + + b.Property("PredecessorId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("RetryCount") + .HasColumnType("integer"); + + b.Property("Scope") + .HasColumnType("text"); + + b.Property("SleepUntil") + .HasColumnType("timestamp without time zone"); + + b.Property("StartTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("StepId") + .HasColumnType("integer"); + + b.Property("StepName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("WorkflowId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AttributeKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("AttributeValue") + .HasColumnType("text"); + + b.Property("ExecutionPointerId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedScheduledCommand", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CommandName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Data") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ExecuteTime") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecuteTime"); + + b.HasIndex("CommandName", "Data") + .IsUnique(); + + b.ToTable("PersistedScheduledCommand"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExecutionPointerId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExternalToken") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExternalTokenExpiry") + .HasColumnType("timestamp without time zone"); + + b.Property("ExternalWorkerId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("StepId") + .HasColumnType("integer"); + + b.Property("SubscribeAsOf") + .HasColumnType("timestamp without time zone"); + + b.Property("SubscriptionData") + .HasColumnType("text"); + + b.Property("SubscriptionId") + .HasMaxLength(200) + .HasColumnType("uuid"); + + b.Property("WorkflowId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CompleteTime") + .HasColumnType("timestamp without time zone"); + + b.Property("CreateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("InstanceId") + .HasMaxLength(200) + .HasColumnType("uuid"); + + b.Property("NextExecution") + .HasColumnType("bigint"); + + b.Property("Reference") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Version") + .HasColumnType("integer"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Workflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Navigation("ExtensionAttributes"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Navigation("ExecutionPointers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.cs new file mode 100644 index 000000000..23eff0eb2 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace WorkflowCore.Persistence.PostgreSQL.Migrations +{ + public partial class scheduledcommands : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "PersistedScheduledCommand", + columns: table => new + { + PersistenceId = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CommandName = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + Data = table.Column(type: "character varying(500)", maxLength: 500, nullable: true), + ExecuteTime = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PersistedScheduledCommand", x => x.PersistenceId); + }); + + migrationBuilder.CreateIndex( + name: "IX_PersistedScheduledCommand_CommandName_Data", + table: "PersistedScheduledCommand", + columns: new[] { "CommandName", "Data" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_PersistedScheduledCommand_ExecuteTime", + table: "PersistedScheduledCommand", + column: "ExecuteTime"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "PersistedScheduledCommand"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs index 6def22497..76c681d8d 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs @@ -2,7 +2,9 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using WorkflowCore.Persistence.PostgreSQL; namespace WorkflowCore.Persistence.PostgreSQL.Migrations { @@ -13,9 +15,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) - .HasAnnotation("ProductVersion", "3.1.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.8") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => { @@ -31,12 +33,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b.Property("EventKey") - .HasColumnType("character varying(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("character varying(200)"); b.Property("EventName") - .HasColumnType("character varying(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("character varying(200)"); b.Property("EventTime") .HasColumnType("timestamp without time zone"); @@ -55,7 +57,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("EventName", "EventKey"); - b.ToTable("Event","wfc"); + b.ToTable("Event", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => @@ -69,19 +71,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp without time zone"); b.Property("ExecutionPointerId") - .HasColumnType("character varying(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("character varying(100)"); b.Property("Message") .HasColumnType("text"); b.Property("WorkflowId") - .HasColumnType("character varying(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("character varying(100)"); b.HasKey("PersistenceId"); - b.ToTable("ExecutionError","wfc"); + b.ToTable("ExecutionError", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => @@ -107,19 +109,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b.Property("EventKey") - .HasColumnType("character varying(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("character varying(100)"); b.Property("EventName") - .HasColumnType("character varying(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("character varying(100)"); b.Property("EventPublished") .HasColumnType("boolean"); b.Property("Id") - .HasColumnType("character varying(50)") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("character varying(50)"); b.Property("Outcome") .HasColumnType("text"); @@ -128,8 +130,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b.Property("PredecessorId") - .HasColumnType("character varying(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("character varying(100)"); b.Property("RetryCount") .HasColumnType("integer"); @@ -150,8 +152,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("integer"); b.Property("StepName") - .HasColumnType("character varying(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("character varying(100)"); b.Property("WorkflowId") .HasColumnType("bigint"); @@ -160,7 +162,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("WorkflowId"); - b.ToTable("ExecutionPointer","wfc"); + b.ToTable("ExecutionPointer", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => @@ -171,8 +173,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("AttributeKey") - .HasColumnType("character varying(100)") - .HasMaxLength(100); + .HasMaxLength(100) + .HasColumnType("character varying(100)"); b.Property("AttributeValue") .HasColumnType("text"); @@ -184,7 +186,35 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ExecutionPointerId"); - b.ToTable("ExtensionAttribute","wfc"); + b.ToTable("ExtensionAttribute", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedScheduledCommand", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CommandName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Data") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ExecuteTime") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecuteTime"); + + b.HasIndex("CommandName", "Data") + .IsUnique(); + + b.ToTable("PersistedScheduledCommand"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => @@ -195,27 +225,27 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("EventKey") - .HasColumnType("character varying(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("character varying(200)"); b.Property("EventName") - .HasColumnType("character varying(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("character varying(200)"); b.Property("ExecutionPointerId") - .HasColumnType("character varying(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("character varying(200)"); b.Property("ExternalToken") - .HasColumnType("character varying(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("character varying(200)"); b.Property("ExternalTokenExpiry") .HasColumnType("timestamp without time zone"); b.Property("ExternalWorkerId") - .HasColumnType("character varying(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("character varying(200)"); b.Property("StepId") .HasColumnType("integer"); @@ -227,12 +257,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b.Property("SubscriptionId") - .HasColumnType("uuid") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("uuid"); b.Property("WorkflowId") - .HasColumnType("character varying(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("character varying(200)"); b.HasKey("PersistenceId"); @@ -243,7 +273,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("SubscriptionId") .IsUnique(); - b.ToTable("Subscription","wfc"); + b.ToTable("Subscription", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => @@ -263,19 +293,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b.Property("Description") - .HasColumnType("character varying(500)") - .HasMaxLength(500); + .HasMaxLength(500) + .HasColumnType("character varying(500)"); b.Property("InstanceId") - .HasColumnType("uuid") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("uuid"); b.Property("NextExecution") .HasColumnType("bigint"); b.Property("Reference") - .HasColumnType("character varying(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("character varying(200)"); b.Property("Status") .HasColumnType("integer"); @@ -284,8 +314,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("integer"); b.Property("WorkflowDefinitionId") - .HasColumnType("character varying(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("character varying(200)"); b.HasKey("PersistenceId"); @@ -294,7 +324,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("NextExecution"); - b.ToTable("Workflow","wfc"); + b.ToTable("Workflow", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => @@ -304,6 +334,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("WorkflowId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("Workflow"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => @@ -313,6 +345,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("ExecutionPointerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Navigation("ExtensionAttributes"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Navigation("ExecutionPointers"); }); #pragma warning restore 612, 618 } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.Designer.cs new file mode 100644 index 000000000..489276722 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.Designer.cs @@ -0,0 +1,379 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WorkflowCore.Persistence.SqlServer; + +namespace WorkflowCore.Persistence.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerContext))] + [Migration("20211015215114_scheduled-commands")] + partial class scheduledcommands + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.8") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("EventData") + .HasColumnType("nvarchar(max)"); + + b.Property("EventId") + .HasColumnType("uniqueidentifier"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("EventTime") + .HasColumnType("datetime2"); + + b.Property("IsProcessed") + .HasColumnType("bit"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventId") + .IsUnique(); + + b.HasIndex("EventTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("Event", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionError", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ErrorTime") + .HasColumnType("datetime2"); + + b.Property("ExecutionPointerId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Message") + .HasColumnType("nvarchar(max)"); + + b.Property("WorkflowId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("PersistenceId"); + + b.ToTable("ExecutionError", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Children") + .HasColumnType("nvarchar(max)"); + + b.Property("ContextItem") + .HasColumnType("nvarchar(max)"); + + b.Property("EndTime") + .HasColumnType("datetime2"); + + b.Property("EventData") + .HasColumnType("nvarchar(max)"); + + b.Property("EventKey") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("EventName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("EventPublished") + .HasColumnType("bit"); + + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Outcome") + .HasColumnType("nvarchar(max)"); + + b.Property("PersistenceData") + .HasColumnType("nvarchar(max)"); + + b.Property("PredecessorId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Scope") + .HasColumnType("nvarchar(max)"); + + b.Property("SleepUntil") + .HasColumnType("datetime2"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("StepName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("WorkflowId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("WorkflowId"); + + b.ToTable("ExecutionPointer", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AttributeKey") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("AttributeValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutionPointerId") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("ExtensionAttribute", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedScheduledCommand", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CommandName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Data") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ExecuteTime") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecuteTime"); + + b.HasIndex("CommandName", "Data") + .IsUnique() + .HasFilter("[CommandName] IS NOT NULL AND [Data] IS NOT NULL"); + + b.ToTable("PersistedScheduledCommand"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ExecutionPointerId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ExternalToken") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ExternalTokenExpiry") + .HasColumnType("datetime2"); + + b.Property("ExternalWorkerId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("SubscribeAsOf") + .HasColumnType("datetime2"); + + b.Property("SubscriptionData") + .HasColumnType("nvarchar(max)"); + + b.Property("SubscriptionId") + .HasMaxLength(200) + .HasColumnType("uniqueidentifier"); + + b.Property("WorkflowId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("PersistenceId"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.HasIndex("SubscriptionId") + .IsUnique(); + + b.ToTable("Subscription", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CompleteTime") + .HasColumnType("datetime2"); + + b.Property("CreateTime") + .HasColumnType("datetime2"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("InstanceId") + .HasMaxLength(200) + .HasColumnType("uniqueidentifier"); + + b.Property("NextExecution") + .HasColumnType("bigint"); + + b.Property("Reference") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Version") + .HasColumnType("int"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("PersistenceId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.HasIndex("NextExecution"); + + b.ToTable("Workflow", "wfc"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Workflow"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExtensionAttribute", b => + { + b.HasOne("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ExecutionPointer"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedExecutionPointer", b => + { + b.Navigation("ExtensionAttributes"); + }); + + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedWorkflow", b => + { + b.Navigation("ExecutionPointers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.cs new file mode 100644 index 000000000..357fbe2f1 --- /dev/null +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WorkflowCore.Persistence.SqlServer.Migrations +{ + public partial class scheduledcommands : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "PersistedScheduledCommand", + columns: table => new + { + PersistenceId = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + CommandName = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + Data = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), + ExecuteTime = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PersistedScheduledCommand", x => x.PersistenceId); + }); + + migrationBuilder.CreateIndex( + name: "IX_PersistedScheduledCommand_CommandName_Data", + table: "PersistedScheduledCommand", + columns: new[] { "CommandName", "Data" }, + unique: true, + filter: "[CommandName] IS NOT NULL AND [Data] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_PersistedScheduledCommand_ExecuteTime", + table: "PersistedScheduledCommand", + column: "ExecuteTime"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "PersistedScheduledCommand"); + } + } +} diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs index 2e32d838d..df593d220 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs @@ -2,6 +2,9 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WorkflowCore.Persistence.SqlServer; namespace WorkflowCore.Persistence.SqlServer.Migrations { @@ -12,16 +15,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .UseIdentityColumns() .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.1"); + .HasAnnotation("ProductVersion", "5.0.8") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedEvent", b => { b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .UseIdentityColumn(); + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("EventData") .HasColumnType("nvarchar(max)"); @@ -62,7 +67,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .UseIdentityColumn(); + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ErrorTime") .HasColumnType("datetime2"); @@ -88,7 +95,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .UseIdentityColumn(); + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Active") .HasColumnType("bit"); @@ -167,7 +176,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .UseIdentityColumn(); + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("AttributeKey") .HasMaxLength(100) @@ -186,12 +197,43 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("ExtensionAttribute", "wfc"); }); + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedScheduledCommand", b => + { + b.Property("PersistenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CommandName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Data") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ExecuteTime") + .HasColumnType("bigint"); + + b.HasKey("PersistenceId"); + + b.HasIndex("ExecuteTime"); + + b.HasIndex("CommandName", "Data") + .IsUnique() + .HasFilter("[CommandName] IS NOT NULL AND [Data] IS NOT NULL"); + + b.ToTable("PersistedScheduledCommand"); + }); + modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => { b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .UseIdentityColumn(); + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("EventKey") .HasMaxLength(200) @@ -250,7 +292,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") - .UseIdentityColumn(); + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("CompleteTime") .HasColumnType("datetime2"); diff --git a/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDelayScenario.cs b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDelayScenario.cs new file mode 100644 index 000000000..deed80f2b --- /dev/null +++ b/test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDelayScenario.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using WorkflowCore.IntegrationTests.Scenarios; +using Xunit; + +namespace WorkflowCore.Tests.MySQL.Scenarios +{ + [Collection("Mysql collection")] + public class MysqlDelayScenario : DelayScenario + { + protected override void ConfigureServices(IServiceCollection services) + { + services.AddWorkflow(cfg => + { + cfg.UseMySQL(MysqlDockerSetup.ScenarioConnectionString, true, true); + cfg.UsePollInterval(TimeSpan.FromSeconds(2)); + }); + } + } +} From 94e70d70d2903e37378e3c3eb95c1e2e1e3db4ef Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 22 Oct 2021 19:04:18 -0700 Subject: [PATCH 383/462] release notes --- ReleaseNotes/3.7.0.md | 8 ++++++++ WorkflowCore.sln | 2 ++ 2 files changed, 10 insertions(+) create mode 100644 ReleaseNotes/3.7.0.md diff --git a/ReleaseNotes/3.7.0.md b/ReleaseNotes/3.7.0.md new file mode 100644 index 000000000..a6a8ec126 --- /dev/null +++ b/ReleaseNotes/3.7.0.md @@ -0,0 +1,8 @@ +# Workflow Core 3.7.0 + +## Scheduled Commands + +Introduces the ability to schedule delayed commands to process a workflow or event, by persisting them to storage. +This is the first step toward removing constant polling of the DB. It also filters out duplicate work items on the queue which is the current problem the greylist tries to solve. +Initial implementation is supported by MongoDb, SQL Server, PostgeSQL, MySQL and SQLite. +Additional support from the other persistence providers to follow. diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 5893af37d..cf389574e 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -104,6 +104,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\3.0.0.md = ReleaseNotes\3.0.0.md ReleaseNotes\3.1.0.md = ReleaseNotes\3.1.0.md ReleaseNotes\3.3.0.md = ReleaseNotes\3.3.0.md + ReleaseNotes\3.4.0.md = ReleaseNotes\3.4.0.md + ReleaseNotes\3.7.0.md = ReleaseNotes\3.7.0.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" From b34690c5142c0f5bc86f50e0c97c4e41d7bbb72d Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 22 Oct 2021 19:19:42 -0700 Subject: [PATCH 384/462] bug --- .../Services/EntityFrameworkPersistenceProvider.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs index 56dd7f90c..44798a0c9 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs @@ -398,8 +398,9 @@ public async Task ProcessCommands(DateTimeOffset asOf, Func().Remove(command); - await db.SaveChangesAsync(); + using var db2 = ConstructDbContext(); + db2.Set().Remove(command); + await db2.SaveChangesAsync(); } catch (Exception) { From c42313c90fa859ab43628ed15de39c14a0bb9fb3 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 23 Oct 2021 09:09:03 -0700 Subject: [PATCH 385/462] table name --- .../Services/WorkflowDbContext.cs | 2 ++ .../WorkflowCore.Persistence.MySQL/MysqlContext.cs | 6 ++++++ .../WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs | 6 ++++++ .../WorkflowCore.Persistence.SqlServer/SqlServerContext.cs | 6 ++++++ .../WorkflowCore.Persistence.Sqlite/SqliteContext.cs | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs index 645a26c3d..53e0967e7 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/WorkflowDbContext.cs @@ -14,6 +14,7 @@ public abstract class WorkflowDbContext : DbContext protected abstract void ConfigureExetensionAttributeStorage(EntityTypeBuilder builder); protected abstract void ConfigureSubscriptionStorage(EntityTypeBuilder builder); protected abstract void ConfigureEventStorage(EntityTypeBuilder builder); + protected abstract void ConfigureScheduledCommandStorage(EntityTypeBuilder builder); protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -48,6 +49,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ConfigureExetensionAttributeStorage(extensionAttributes); ConfigureSubscriptionStorage(subscriptions); ConfigureEventStorage(events); + ConfigureScheduledCommandStorage(commands); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) diff --git a/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs index a6ebba9c2..dc49617fd 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs @@ -63,5 +63,11 @@ protected override void ConfigureEventStorage(EntityTypeBuilder builder.ToTable("Event"); builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); } + + protected override void ConfigureScheduledCommandStorage(EntityTypeBuilder builder) + { + builder.ToTable("ScheduledCommand"); + builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); + } } } diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs index 17e83e958..3eb80b448 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/PostgresContext.cs @@ -60,6 +60,12 @@ protected override void ConfigureEventStorage(EntityTypeBuilder builder.ToTable("Event", _schemaName); builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); } + + protected override void ConfigureScheduledCommandStorage(EntityTypeBuilder builder) + { + builder.ToTable("ScheduledCommand", _schemaName); + builder.Property(x => x.PersistenceId).ValueGeneratedOnAdd(); + } } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs index 6e2656a5b..eb03f3647 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/SqlServerContext.cs @@ -58,5 +58,11 @@ protected override void ConfigureEventStorage(EntityTypeBuilder builder.ToTable("Event", "wfc"); builder.Property(x => x.PersistenceId).UseIdentityColumn(); } + + protected override void ConfigureScheduledCommandStorage(EntityTypeBuilder builder) + { + builder.ToTable("ScheduledCommand", "wfc"); + builder.Property(x => x.PersistenceId).UseIdentityColumn(); + } } } diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs b/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs index 1b5a6c469..0f2083933 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs +++ b/src/providers/WorkflowCore.Persistence.Sqlite/SqliteContext.cs @@ -52,5 +52,10 @@ protected override void ConfigureEventStorage(EntityTypeBuilder { builder.ToTable("Event"); } + + protected override void ConfigureScheduledCommandStorage(EntityTypeBuilder builder) + { + builder.ToTable("ScheduledCommand"); + } } } From 74e046767ba6f668818c2a81d5f31a12929957ff Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 23 Oct 2021 09:21:35 -0700 Subject: [PATCH 386/462] migration --- ...11023161949_scheduled-commands.Designer.cs} | 4 ++-- ...cs => 20211023161949_scheduled-commands.cs} | 14 +++++++------- .../MysqlPersistenceProviderModelSnapshot.cs | 2 +- ...11023161649_scheduled-commands.Designer.cs} | 4 ++-- ...cs => 20211023161649_scheduled-commands.cs} | 18 +++++++++++------- ...PostgresPersistenceProviderModelSnapshot.cs | 2 +- ...11023161544_scheduled-commands.Designer.cs} | 6 ++++-- ...cs => 20211023161544_scheduled-commands.cs} | 18 +++++++++++------- ...qlServerPersistenceProviderModelSnapshot.cs | 4 +++- 9 files changed, 42 insertions(+), 30 deletions(-) rename src/providers/WorkflowCore.Persistence.MySQL/Migrations/{20211015215708_scheduled-commands.Designer.cs => 20211023161949_scheduled-commands.Designer.cs} (99%) rename src/providers/WorkflowCore.Persistence.MySQL/Migrations/{20211015215708_scheduled-commands.cs => 20211023161949_scheduled-commands.cs} (96%) rename src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/{20211015204734_scheduled-commands.Designer.cs => 20211023161649_scheduled-commands.Designer.cs} (99%) rename src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/{20211015204734_scheduled-commands.cs => 20211023161649_scheduled-commands.cs} (73%) rename src/providers/WorkflowCore.Persistence.SqlServer/Migrations/{20211015215114_scheduled-commands.Designer.cs => 20211023161544_scheduled-commands.Designer.cs} (98%) rename src/providers/WorkflowCore.Persistence.SqlServer/Migrations/{20211015215114_scheduled-commands.cs => 20211023161544_scheduled-commands.cs} (72%) diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.Designer.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211023161949_scheduled-commands.Designer.cs similarity index 99% rename from src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.Designer.cs rename to src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211023161949_scheduled-commands.Designer.cs index 9573baf3e..94222e8d5 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211023161949_scheduled-commands.Designer.cs @@ -9,7 +9,7 @@ namespace WorkflowCore.Persistence.MySQL.Migrations { [DbContext(typeof(MysqlContext))] - [Migration("20211015215708_scheduled-commands")] + [Migration("20211023161949_scheduled-commands")] partial class scheduledcommands { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -209,7 +209,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("CommandName", "Data") .IsUnique(); - b.ToTable("PersistedScheduledCommand"); + b.ToTable("ScheduledCommand"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211023161949_scheduled-commands.cs similarity index 96% rename from src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.cs rename to src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211023161949_scheduled-commands.cs index b08be15b8..b06303070 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211015215708_scheduled-commands.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/20211023161949_scheduled-commands.cs @@ -154,7 +154,7 @@ protected override void Up(MigrationBuilder migrationBuilder) .OldAnnotation("MySql:CharSet", "utf8mb4"); migrationBuilder.CreateTable( - name: "PersistedScheduledCommand", + name: "ScheduledCommand", columns: table => new { PersistenceId = table.Column(type: "bigint", nullable: false) @@ -167,26 +167,26 @@ protected override void Up(MigrationBuilder migrationBuilder) }, constraints: table => { - table.PrimaryKey("PK_PersistedScheduledCommand", x => x.PersistenceId); + table.PrimaryKey("PK_ScheduledCommand", x => x.PersistenceId); }) .Annotation("MySql:CharSet", "utf8mb4"); migrationBuilder.CreateIndex( - name: "IX_PersistedScheduledCommand_CommandName_Data", - table: "PersistedScheduledCommand", + name: "IX_ScheduledCommand_CommandName_Data", + table: "ScheduledCommand", columns: new[] { "CommandName", "Data" }, unique: true); migrationBuilder.CreateIndex( - name: "IX_PersistedScheduledCommand_ExecuteTime", - table: "PersistedScheduledCommand", + name: "IX_ScheduledCommand_ExecuteTime", + table: "ScheduledCommand", column: "ExecuteTime"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "PersistedScheduledCommand"); + name: "ScheduledCommand"); migrationBuilder.AlterColumn( name: "InstanceId", diff --git a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs index cd2b9ceeb..8a3e02e9b 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/Migrations/MysqlPersistenceProviderModelSnapshot.cs @@ -207,7 +207,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("CommandName", "Data") .IsUnique(); - b.ToTable("PersistedScheduledCommand"); + b.ToTable("ScheduledCommand"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.Designer.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211023161649_scheduled-commands.Designer.cs similarity index 99% rename from src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.Designer.cs rename to src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211023161649_scheduled-commands.Designer.cs index caa2f0941..0608d5e41 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211023161649_scheduled-commands.Designer.cs @@ -10,7 +10,7 @@ namespace WorkflowCore.Persistence.PostgreSQL.Migrations { [DbContext(typeof(PostgresContext))] - [Migration("20211015204734_scheduled-commands")] + [Migration("20211023161649_scheduled-commands")] partial class scheduledcommands { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -216,7 +216,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("CommandName", "Data") .IsUnique(); - b.ToTable("PersistedScheduledCommand"); + b.ToTable("ScheduledCommand", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211023161649_scheduled-commands.cs similarity index 73% rename from src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.cs rename to src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211023161649_scheduled-commands.cs index 23eff0eb2..0f1302181 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211015204734_scheduled-commands.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/20211023161649_scheduled-commands.cs @@ -8,7 +8,8 @@ public partial class scheduledcommands : Migration protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "PersistedScheduledCommand", + name: "ScheduledCommand", + schema: "wfc", columns: table => new { PersistenceId = table.Column(type: "bigint", nullable: false) @@ -19,25 +20,28 @@ protected override void Up(MigrationBuilder migrationBuilder) }, constraints: table => { - table.PrimaryKey("PK_PersistedScheduledCommand", x => x.PersistenceId); + table.PrimaryKey("PK_ScheduledCommand", x => x.PersistenceId); }); migrationBuilder.CreateIndex( - name: "IX_PersistedScheduledCommand_CommandName_Data", - table: "PersistedScheduledCommand", + name: "IX_ScheduledCommand_CommandName_Data", + schema: "wfc", + table: "ScheduledCommand", columns: new[] { "CommandName", "Data" }, unique: true); migrationBuilder.CreateIndex( - name: "IX_PersistedScheduledCommand_ExecuteTime", - table: "PersistedScheduledCommand", + name: "IX_ScheduledCommand_ExecuteTime", + schema: "wfc", + table: "ScheduledCommand", column: "ExecuteTime"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "PersistedScheduledCommand"); + name: "ScheduledCommand", + schema: "wfc"); } } } diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs index 76c681d8d..d0278e0c8 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/Migrations/PostgresPersistenceProviderModelSnapshot.cs @@ -214,7 +214,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("CommandName", "Data") .IsUnique(); - b.ToTable("PersistedScheduledCommand"); + b.ToTable("ScheduledCommand", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.Designer.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211023161544_scheduled-commands.Designer.cs similarity index 98% rename from src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.Designer.cs rename to src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211023161544_scheduled-commands.Designer.cs index 489276722..84bed0be0 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.Designer.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211023161544_scheduled-commands.Designer.cs @@ -10,7 +10,7 @@ namespace WorkflowCore.Persistence.SqlServer.Migrations { [DbContext(typeof(SqlServerContext))] - [Migration("20211015215114_scheduled-commands")] + [Migration("20211023161544_scheduled-commands")] partial class scheduledcommands { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -204,6 +204,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("CommandName") @@ -225,7 +227,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .IsUnique() .HasFilter("[CommandName] IS NOT NULL AND [Data] IS NOT NULL"); - b.ToTable("PersistedScheduledCommand"); + b.ToTable("ScheduledCommand", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211023161544_scheduled-commands.cs similarity index 72% rename from src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.cs rename to src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211023161544_scheduled-commands.cs index 357fbe2f1..2d5ec1572 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211015215114_scheduled-commands.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/20211023161544_scheduled-commands.cs @@ -7,7 +7,8 @@ public partial class scheduledcommands : Migration protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "PersistedScheduledCommand", + name: "ScheduledCommand", + schema: "wfc", columns: table => new { PersistenceId = table.Column(type: "bigint", nullable: false) @@ -18,26 +19,29 @@ protected override void Up(MigrationBuilder migrationBuilder) }, constraints: table => { - table.PrimaryKey("PK_PersistedScheduledCommand", x => x.PersistenceId); + table.PrimaryKey("PK_ScheduledCommand", x => x.PersistenceId); }); migrationBuilder.CreateIndex( - name: "IX_PersistedScheduledCommand_CommandName_Data", - table: "PersistedScheduledCommand", + name: "IX_ScheduledCommand_CommandName_Data", + schema: "wfc", + table: "ScheduledCommand", columns: new[] { "CommandName", "Data" }, unique: true, filter: "[CommandName] IS NOT NULL AND [Data] IS NOT NULL"); migrationBuilder.CreateIndex( - name: "IX_PersistedScheduledCommand_ExecuteTime", - table: "PersistedScheduledCommand", + name: "IX_ScheduledCommand_ExecuteTime", + schema: "wfc", + table: "ScheduledCommand", column: "ExecuteTime"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "PersistedScheduledCommand"); + name: "ScheduledCommand", + schema: "wfc"); } } } diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs index df593d220..39da38276 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs +++ b/src/providers/WorkflowCore.Persistence.SqlServer/Migrations/SqlServerPersistenceProviderModelSnapshot.cs @@ -202,6 +202,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PersistenceId") .ValueGeneratedOnAdd() .HasColumnType("bigint") + .HasAnnotation("SqlServer:IdentityIncrement", 1) + .HasAnnotation("SqlServer:IdentitySeed", 1) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("CommandName") @@ -223,7 +225,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsUnique() .HasFilter("[CommandName] IS NOT NULL AND [Data] IS NOT NULL"); - b.ToTable("PersistedScheduledCommand"); + b.ToTable("ScheduledCommand", "wfc"); }); modelBuilder.Entity("WorkflowCore.Persistence.EntityFramework.Models.PersistedSubscription", b => From eb28727f2d0c55af0acefe78d12a25bd5b45e616 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 31 Oct 2021 10:08:29 -0700 Subject: [PATCH 387/462] release notes --- ReleaseNotes/{3.7.0.md => 3.6.0.md} | 2 +- WorkflowCore.sln | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename ReleaseNotes/{3.7.0.md => 3.6.0.md} (95%) diff --git a/ReleaseNotes/3.7.0.md b/ReleaseNotes/3.6.0.md similarity index 95% rename from ReleaseNotes/3.7.0.md rename to ReleaseNotes/3.6.0.md index a6a8ec126..697976a46 100644 --- a/ReleaseNotes/3.7.0.md +++ b/ReleaseNotes/3.6.0.md @@ -1,4 +1,4 @@ -# Workflow Core 3.7.0 +# Workflow Core 3.6.0 ## Scheduled Commands diff --git a/WorkflowCore.sln b/WorkflowCore.sln index cf389574e..26bc01cf6 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -105,7 +105,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote ReleaseNotes\3.1.0.md = ReleaseNotes\3.1.0.md ReleaseNotes\3.3.0.md = ReleaseNotes\3.3.0.md ReleaseNotes\3.4.0.md = ReleaseNotes\3.4.0.md - ReleaseNotes\3.7.0.md = ReleaseNotes\3.7.0.md + ReleaseNotes\3.6.0.md = ReleaseNotes\3.6.0.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" From a03137b04e3f1ad670b660bb9a3f1e3da6fe43f7 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 10 Nov 2021 11:45:51 -0800 Subject: [PATCH 388/462] fix bug --- src/Directory.Build.props | 8 ++++---- .../Services/BackgroundTasks/RunnablePoller.cs | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7c162693d..f95b297ab 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.6.0 - 3.6.0.0 - 3.6.0.0 + 3.6.1 + 3.6.1.0 + 3.6.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.6.0 + 3.6.1 diff --git a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs index b40a7a208..24b2af520 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs @@ -161,6 +161,9 @@ private async Task PollCommands() { try { + if (!_persistenceStore.SupportsScheduledCommands) + return; + if (await _lockProvider.AcquireLock("poll-commands", new CancellationToken())) { try From 8191dbae84713dd53c21a254afd182ace794975b Mon Sep 17 00:00:00 2001 From: Sergii Kram Date: Fri, 26 Nov 2021 15:52:12 +0200 Subject: [PATCH 389/462] Fix GitHub actions build. --- .github/workflows/dotnet.yml | 14 +++++++------- src/WorkflowCore/WorkflowCore.csproj | 4 +++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 12b398572..8ede77162 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,20 +16,20 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.* + dotnet-version: 3.1.* - name: Restore dependencies run: dotnet restore - name: Build run: dotnet build --no-restore - name: Unit Tests - run: dotnet test test/WorkflowCore.UnitTests --no-build --verbosity normal + run: dotnet test test/WorkflowCore.UnitTests --no-build --verbosity normal -p:ParallelizeTestCollections=false - name: Integration Tests - run: dotnet test test/WorkflowCore.IntegrationTests --no-build --verbosity normal + run: dotnet test test/WorkflowCore.IntegrationTests --no-build --verbosity normal -p:ParallelizeTestCollections=false - name: PostgreSQL Tests - run: dotnet test test/WorkflowCore.Tests.PostgreSQL --no-build --verbosity normal + run: dotnet test test/WorkflowCore.Tests.PostgreSQL --no-build --verbosity normal -p:ParallelizeTestCollections=false - name: Redis Tests - run: dotnet test test/WorkflowCore.Tests.Redis --no-build --verbosity normal + run: dotnet test test/WorkflowCore.Tests.Redis --no-build --verbosity normal -p:ParallelizeTestCollections=false - name: SQL Server Tests - run: dotnet test test/WorkflowCore.Tests.SqlServer --no-build --verbosity normal + run: dotnet test test/WorkflowCore.Tests.SqlServer --no-build --verbosity normal -p:ParallelizeTestCollections=false - name: Elasticsearch Tests - run: dotnet test test/WorkflowCore.Tests.Elasticsearch --no-build --verbosity normal + run: dotnet test test/WorkflowCore.Tests.Elasticsearch --no-build --verbosity normal -p:ParallelizeTestCollections=false diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 800ef9c08..7092270cf 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -26,7 +26,9 @@ - + + <_Parameter1>WorkflowCore.IntegrationTests + From 3066e05280e390858eb579cbd2c30c61f1e14929 Mon Sep 17 00:00:00 2001 From: potatopeelings Date: Sat, 18 Dec 2021 20:46:35 +1100 Subject: [PATCH 390/462] Resolve #957, E11000 duplicate key error --- .../Services/MongoPersistenceProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index ac65eb39b..e0147f5a4 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -315,9 +315,9 @@ public async Task ScheduleCommand(ScheduledCommand command) { await ScheduledCommands.InsertOneAsync(command); } - catch (MongoBulkWriteException ex) + catch (MongoWriteException ex) { - if (ex.WriteErrors.All(x => x.Category == ServerErrorCategory.DuplicateKey)) + if (ex.WriteError?.Category == ServerErrorCategory.DuplicateKey) return; throw; } From 23f5f274ca44800ab86e1cd9af916ded73e5733c Mon Sep 17 00:00:00 2001 From: potatopeelings Date: Mon, 20 Dec 2021 09:06:46 +1100 Subject: [PATCH 391/462] Resolve #957, Add back MongoBulkWriteException --- .../Services/MongoPersistenceProvider.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index e0147f5a4..06be13355 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -321,6 +321,12 @@ public async Task ScheduleCommand(ScheduledCommand command) return; throw; } + catch (MongoBulkWriteException ex) + { + if (ex.WriteErrors.All(x => x.Category == ServerErrorCategory.DuplicateKey)) + return; + throw; + } } public async Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) From 40787a148ec0d52c7cce49c1efa9eb5560361447 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 20 Dec 2021 11:04:00 -0800 Subject: [PATCH 392/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index f95b297ab..29d5fdae3 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.6.1 - 3.6.1.0 - 3.6.1.0 + 3.6.2 + 3.6.2.0 + 3.6.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.6.1 + 3.6.2 From 17610b3cd5a7097ea3e33aa60cf53ab6332a9d8e Mon Sep 17 00:00:00 2001 From: Gabriel Lucaci Date: Fri, 18 Feb 2022 16:40:36 +0100 Subject: [PATCH 393/462] Add ActivitySource/OpenTelemetry support --- src/WorkflowCore/Models/WorkflowOptions.cs | 5 + .../Services/BackgroundTasks/EventConsumer.cs | 2 + .../Services/BackgroundTasks/IndexConsumer.cs | 2 + .../Services/BackgroundTasks/QueueConsumer.cs | 20 ++- .../BackgroundTasks/RunnablePoller.cs | 24 +++- .../BackgroundTasks/WorkflowConsumer.cs | 4 + src/WorkflowCore/Services/WorkflowActivity.cs | 124 ++++++++++++++++++ src/WorkflowCore/Services/WorkflowExecutor.cs | 1 + src/WorkflowCore/Services/WorkflowHost.cs | 44 ++++--- src/WorkflowCore/WorkflowCore.csproj | 1 + 10 files changed, 208 insertions(+), 19 deletions(-) create mode 100644 src/WorkflowCore/Services/WorkflowActivity.cs diff --git a/src/WorkflowCore/Models/WorkflowOptions.cs b/src/WorkflowCore/Models/WorkflowOptions.cs index fb1ae9a7e..8913c32c2 100644 --- a/src/WorkflowCore/Models/WorkflowOptions.cs +++ b/src/WorkflowCore/Models/WorkflowOptions.cs @@ -75,6 +75,11 @@ public void UseErrorRetryInterval(TimeSpan interval) ErrorRetryInterval = interval; } + public void UseIdleTime(TimeSpan interval) + { + IdleTime = interval; + } + public void UseMaxConcurrentWorkflows(int maxConcurrentWorkflows) { MaxConcurrentWorkflows = maxConcurrentWorkflows; diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index 9635df3ad..dd7323b01 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -43,6 +43,8 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { cancellationToken.ThrowIfCancellationRequested(); var evt = await _eventRepository.GetEvent(itemId, cancellationToken); + + WorkflowActivity.Enrich(evt); if (evt.IsProcessed) { _greylist.Add($"evt:{evt.Id}"); diff --git a/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs index 10899af10..29565e647 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/IndexConsumer.cs @@ -32,6 +32,8 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance try { var workflow = await FetchWorkflow(itemId); + + WorkflowActivity.Enrich(workflow, "index"); await _searchIndex.IndexWorkflow(workflow); lock (_errorCounts) { diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index b1bb7c5b9..1476e8b96 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using ConcurrentCollections; using Microsoft.Extensions.Logging; +using OpenTelemetry.Trace; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -61,6 +63,7 @@ private async Task Execute() while (!cancelToken.IsCancellationRequested) { + Activity activity = default; try { var activeCount = 0; @@ -74,15 +77,19 @@ private async Task Execute() continue; } + activity = WorkflowActivity.StartConsume(Queue); var item = await QueueProvider.DequeueWork(Queue, cancelToken); if (item == null) { + activity?.Dispose(); if (!QueueProvider.IsDequeueBlocking) await Task.Delay(Options.IdleTime, cancelToken); continue; } + activity?.EnrichWithDequeuedItem(item); + var hasTask = false; lock (_activeTasks) { @@ -93,8 +100,9 @@ private async Task Execute() _secondPasses.Add(item); if (!EnableSecondPasses) await QueueProvider.QueueWork(item, Queue); + activity?.Dispose(); continue; - } + } _secondPasses.TryRemove(item); @@ -103,7 +111,7 @@ private async Task Execute() { _activeTasks.Add(item, waitHandle); } - var task = ExecuteItem(item, waitHandle); + var task = ExecuteItem(item, waitHandle, activity); } catch (OperationCanceledException) { @@ -111,6 +119,11 @@ private async Task Execute() catch (Exception ex) { Logger.LogError(ex, ex.Message); + activity?.RecordException(ex); + } + finally + { + activity?.Dispose(); } } @@ -124,7 +137,7 @@ private async Task Execute() handle.WaitOne(); } - private async Task ExecuteItem(string itemId, EventWaitHandle waitHandle) + private async Task ExecuteItem(string itemId, EventWaitHandle waitHandle, Activity activity) { try { @@ -142,6 +155,7 @@ private async Task ExecuteItem(string itemId, EventWaitHandle waitHandle) catch (Exception ex) { Logger.LogError(default(EventId), ex, $"Error executing item {itemId} - {ex.Message}"); + activity?.RecordException(ex); } finally { diff --git a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs index d7ec5e595..fcd2abd92 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using OpenTelemetry.Trace; using WorkflowCore.Interface; using WorkflowCore.Models; @@ -57,13 +59,14 @@ private async void PollRunnables(object target) private async Task PollWorkflows() { + var activity = WorkflowActivity.StartPoll("workflows"); try { if (await _lockProvider.AcquireLock("poll runnables", new CancellationToken())) { try { - _logger.LogDebug("Polling for runnable workflows"); + _logger.LogDebug("Polling for runnable workflows"); var runnables = await _persistenceStore.GetRunnableInstances(_dateTimeProvider.Now); foreach (var item in runnables) @@ -83,6 +86,7 @@ await _persistenceStore.ScheduleCommand(new ScheduledCommand() catch (Exception ex) { _logger.LogError(ex, ex.Message); + activity?.RecordException(ex); } } if (_greylist.Contains($"wf:{item}")) @@ -104,11 +108,17 @@ await _persistenceStore.ScheduleCommand(new ScheduledCommand() catch (Exception ex) { _logger.LogError(ex, ex.Message); + activity?.RecordException(ex); + } + finally + { + activity?.Dispose(); } } private async Task PollEvents() { + var activity = WorkflowActivity.StartPoll("events"); try { if (await _lockProvider.AcquireLock("unprocessed events", new CancellationToken())) @@ -135,6 +145,7 @@ await _persistenceStore.ScheduleCommand(new ScheduledCommand() catch (Exception ex) { _logger.LogError(ex, ex.Message); + activity?.RecordException(ex); } } if (_greylist.Contains($"evt:{item}")) @@ -156,11 +167,17 @@ await _persistenceStore.ScheduleCommand(new ScheduledCommand() catch (Exception ex) { _logger.LogError(ex, ex.Message); + activity?.RecordException(ex); + } + finally + { + activity?.Dispose(); } } private async Task PollCommands() { + var activity = WorkflowActivity.StartPoll("commands"); try { if (!_persistenceStore.SupportsScheduledCommands) @@ -193,6 +210,11 @@ await _persistenceStore.ProcessCommands(new DateTimeOffset(_dateTimeProvider.Utc catch (Exception ex) { _logger.LogError(ex, ex.Message); + activity?.RecordException(ex); + } + finally + { + activity?.Dispose(); } } } diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index a387848b6..b3dd4aa0b 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -43,6 +44,8 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { cancellationToken.ThrowIfCancellationRequested(); workflow = await _persistenceStore.GetWorkflowInstance(itemId, cancellationToken); + + WorkflowActivity.Enrich(workflow, "process"); if (workflow.Status == WorkflowStatus.Runnable) { try @@ -51,6 +54,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance } finally { + WorkflowActivity.Enrich(result); await _persistenceStore.PersistWorkflow(workflow, cancellationToken); await QueueProvider.QueueWork(itemId, QueueType.Index); _greylist.Remove($"wf:{itemId}"); diff --git a/src/WorkflowCore/Services/WorkflowActivity.cs b/src/WorkflowCore/Services/WorkflowActivity.cs new file mode 100644 index 000000000..b580f98b6 --- /dev/null +++ b/src/WorkflowCore/Services/WorkflowActivity.cs @@ -0,0 +1,124 @@ +using System.Diagnostics; +using OpenTelemetry.Trace; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace WorkflowCore.Services +{ + internal static class WorkflowActivity + { + private static readonly ActivitySource ActivitySource = new ActivitySource("WorkflowCore"); + + internal static Activity StartHost() + { + var activityName = "workflow start host"; + return ActivitySource.StartRootActivity(activityName, ActivityKind.Internal); + } + + internal static Activity StartConsume(QueueType queueType) + { + var activityName = $"workflow consume {GetQueueType(queueType)}"; + var activity = ActivitySource.StartRootActivity(activityName, ActivityKind.Consumer); + + activity?.SetTag("workflow.queue", queueType); + + return activity; + } + + + internal static Activity StartPoll(string type) + { + var activityName = $"workflow poll {type}"; + var activity = ActivitySource.StartRootActivity(activityName, ActivityKind.Client); + + activity?.SetTag("workflow.poll", type); + + return activity; + } + + internal static void Enrich(WorkflowInstance workflow, string action) + { + var activity = Activity.Current; + if (activity != null) + { + activity.DisplayName = $"workflow {action} {workflow.WorkflowDefinitionId}"; + activity.SetTag("workflow.id", workflow.Id); + activity.SetTag("workflow.definition", workflow.WorkflowDefinitionId); + activity.SetTag("workflow.status", workflow.Status); + } + } + + internal static void Enrich(WorkflowStep workflowStep) + { + var activity = Activity.Current; + if (activity != null) + { + var stepName = string.IsNullOrEmpty(workflowStep.Name) + ? "inline" + : workflowStep.Name; + + activity.DisplayName += $" step {stepName}"; + activity.SetTag("workflow.step.id", workflowStep.Id); + activity.SetTag("workflow.step.name", workflowStep.Name); + activity.SetTag("workflow.step.type", workflowStep.BodyType.Name); + } + } + + internal static void Enrich(WorkflowExecutorResult result) + { + var activity = Activity.Current; + if (activity != null) + { + activity.SetTag("workflow.subscriptions.count", result.Subscriptions.Count); + activity.SetTag("workflow.errors.count", result.Errors.Count); + + if (result.Errors.Count > 0) + { + activity.SetStatus(Status.Error); + activity.SetStatus(ActivityStatusCode.Error); + } + } + } + + internal static void Enrich(Event evt) + { + var activity = Activity.Current; + if (activity != null) + { + activity.DisplayName = $"workflow process {evt.EventName}"; + activity.SetTag("workflow.event.id", evt.Id); + activity.SetTag("workflow.event.name", evt.EventName); + activity.SetTag("workflow.event.processed", evt.IsProcessed); + } + } + + internal static void EnrichWithDequeuedItem(this Activity activity, string item) + { + if (activity != null) + { + activity.SetTag("workflow.queue.item", item); + } + } + + private static Activity StartRootActivity( + this ActivitySource activitySource, + string name, + ActivityKind kind) + { + Activity.Current = null; + + return activitySource.StartActivity(name, kind); + } + + private static string GetQueueType(QueueType queueType) + { + switch (queueType) + { + case QueueType.Workflow: return "workflow"; + case QueueType.Event: return "event"; + case QueueType.Index: return "index"; + default: return "unknown"; + } + } + } +} \ No newline at end of file diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index ebdc818fe..145f02d41 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -73,6 +73,7 @@ public async Task Execute(WorkflowInstance workflow, Can continue; } + WorkflowActivity.Enrich(step); try { if (!InitializeStep(workflow, step, wfResult, def, pointer)) diff --git a/src/WorkflowCore/Services/WorkflowHost.cs b/src/WorkflowCore/Services/WorkflowHost.cs index ca5e04b2f..73c8850fa 100644 --- a/src/WorkflowCore/Services/WorkflowHost.cs +++ b/src/WorkflowCore/Services/WorkflowHost.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using OpenTelemetry.Trace; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Models.LifeCycleEvents; @@ -83,21 +84,34 @@ public void Start() public async Task StartAsync(CancellationToken cancellationToken) { - _shutdown = false; - PersistenceStore.EnsureStoreExists(); - await QueueProvider.Start(); - await LockProvider.Start(); - await _lifeCycleEventHub.Start(); - await _searchIndex.Start(); - - // Event subscriptions are removed when stopping the event hub. - // Add them when starting. - AddEventSubscriptions(); - - Logger.LogInformation("Starting background tasks"); - - foreach (var task in _backgroundTasks) - task.Start(); + var activity = WorkflowActivity.StartHost(); + try + { + _shutdown = false; + PersistenceStore.EnsureStoreExists(); + await QueueProvider.Start(); + await LockProvider.Start(); + await _lifeCycleEventHub.Start(); + await _searchIndex.Start(); + + // Event subscriptions are removed when stopping the event hub. + // Add them when starting. + AddEventSubscriptions(); + + Logger.LogInformation("Starting background tasks"); + + foreach (var task in _backgroundTasks) + task.Start(); + } + catch (Exception ex) + { + activity.RecordException(ex); + throw; + } + finally + { + activity?.Dispose(); + } } public void Stop() diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 7092270cf..4eb4ff1e5 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -26,6 +26,7 @@ + <_Parameter1>WorkflowCore.IntegrationTests From 42b3738739094702855e030db218124017857136 Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 15 Mar 2022 16:46:52 +0100 Subject: [PATCH 394/462] Downgrade OpenTelemetry.Api package to last stable version --- src/WorkflowCore/WorkflowCore.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 4eb4ff1e5..7177f0adf 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -26,7 +26,7 @@ - + <_Parameter1>WorkflowCore.IntegrationTests From 0867f48ba3b509186588926493074fd0988979c1 Mon Sep 17 00:00:00 2001 From: Luis Fernando Bertucci Date: Tue, 15 Mar 2022 16:39:34 -0300 Subject: [PATCH 395/462] Update dependencies from 'WorkflowCore.Providers.AWS': AWSSDK.DynamoDBv2 to 3.7.3.11, AWSSDK.Kinesis to 3.7.1.32 and AWSSDK.SQS to 3.7.2.33 --- .../WorkflowCore.Providers.AWS.csproj | 6 +++--- .../WorkflowCore.Sample04/WorkflowCore.Sample04.csproj | 2 +- .../WorkflowCore.Tests.DynamoDB.csproj | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 49b7c2916..5600452a2 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -14,9 +14,9 @@ - - - + + + diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index ddae958be..fd78be8ac 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -24,7 +24,7 @@ - + diff --git a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index f275a05cf..126ac0099 100644 --- a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -7,7 +7,7 @@ - + From dadac9aae992968c55949d34cfde15bfce154143 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 28 Mar 2022 09:38:10 +0200 Subject: [PATCH 396/462] Fix build --- src/WorkflowCore/WorkflowCore.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 7177f0adf..0a238b8aa 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -27,6 +27,7 @@ + <_Parameter1>WorkflowCore.IntegrationTests From 98c54362b2ecd0700c9bd01eef14c3fcffaaecad Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 28 Mar 2022 10:37:21 +0200 Subject: [PATCH 397/462] Cleanup samples and tests --- WorkflowCore.sln | 2 +- {test => src}/WorkflowCore.Testing/JsonWorkflowTest.cs | 0 .../WorkflowCore.Testing/WorkflowCore.Testing.csproj | 0 {test => src}/WorkflowCore.Testing/WorkflowTest.cs | 0 {test => src}/WorkflowCore.Testing/YamlWorkflowTest.cs | 0 {test => src}/WorkflowCore.Testing/readme.md | 0 src/samples/Directory.Build.props | 7 +++++++ src/samples/WebApiSample/Directory.Build.props | 3 +++ src/samples/WebApiSample/WebApiSample/WebApiSample.csproj | 1 - .../WorkflowCore.Sample01/WorkflowCore.Sample01.csproj | 1 - .../WorkflowCore.Sample02/WorkflowCore.Sample02.csproj | 1 - .../WorkflowCore.Sample03/WorkflowCore.Sample03.csproj | 1 - .../WorkflowCore.Sample04/WorkflowCore.Sample04.csproj | 1 - .../WorkflowCore.Sample05/WorkflowCore.Sample05.csproj | 1 - .../WorkflowCore.Sample06/WorkflowCore.Sample06.csproj | 1 - .../WorkflowCore.Sample07/WorkflowCore.Sample07.csproj | 1 - .../WorkflowCore.Sample08/WorkflowCore.Sample08.csproj | 1 - .../WorkflowCore.Sample09/WorkflowCore.Sample09.csproj | 1 - .../WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj | 1 - .../WorkflowCore.Sample10/WorkflowCore.Sample10.csproj | 1 - .../WorkflowCore.Sample11/WorkflowCore.Sample11.csproj | 1 - .../WorkflowCore.Sample12/WorkflowCore.Sample12.csproj | 1 - .../WorkflowCore.Sample13/WorkflowCore.Sample13.csproj | 1 - .../WorkflowCore.Sample14/WorkflowCore.Sample14.csproj | 1 - .../WorkflowCore.Sample15/WorkflowCore.Sample15.csproj | 1 - .../WorkflowCore.Sample16/WorkflowCore.Sample16.csproj | 1 - .../WorkflowCore.Sample17/WorkflowCore.Sample17.csproj | 1 - .../WorkflowCore.Sample18/WorkflowCore.Sample18.csproj | 1 - .../WorkflowCore.Sample19/WorkflowCore.Sample19.csproj | 1 - .../WorkflowCore.TestSample01.csproj | 6 +----- test/Directory.Build.props | 7 +++++++ test/Docker.Testify/Docker.Testify.csproj | 4 ---- test/ScratchPad/ScratchPad.csproj | 1 - .../WorkflowCore.IntegrationTests.csproj | 3 +-- .../WorkflowCore.TestAssets.csproj | 1 - .../WorkflowCore.Tests.DynamoDB.csproj | 6 ------ .../WorkflowCore.Tests.Elasticsearch.csproj | 6 ------ .../WorkflowCore.Tests.MongoDB.csproj | 3 +-- .../WorkflowCore.Tests.MySQL.csproj | 8 +------- .../WorkflowCore.Tests.PostgreSQL.csproj | 3 +-- .../WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj | 6 ------ .../WorkflowCore.Tests.Redis.csproj | 6 ------ .../WorkflowCore.Tests.SqlServer.csproj | 4 ---- .../WorkflowCore.Tests.Sqlite.csproj | 4 ---- test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj | 1 - 45 files changed, 23 insertions(+), 79 deletions(-) rename {test => src}/WorkflowCore.Testing/JsonWorkflowTest.cs (100%) rename {test => src}/WorkflowCore.Testing/WorkflowCore.Testing.csproj (100%) rename {test => src}/WorkflowCore.Testing/WorkflowTest.cs (100%) rename {test => src}/WorkflowCore.Testing/YamlWorkflowTest.cs (100%) rename {test => src}/WorkflowCore.Testing/readme.md (100%) create mode 100644 src/samples/Directory.Build.props create mode 100644 src/samples/WebApiSample/Directory.Build.props create mode 100644 test/Directory.Build.props diff --git a/WorkflowCore.sln b/WorkflowCore.sln index 26bc01cf6..e7fb81a2e 100644 --- a/WorkflowCore.sln +++ b/WorkflowCore.sln @@ -110,7 +110,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Testing", "test\WorkflowCore.Testing\WorkflowCore.Testing.csproj", "{62A9709E-27DA-42EE-B94F-5AF431D86354}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Testing", "src\WorkflowCore.Testing\WorkflowCore.Testing.csproj", "{62A9709E-27DA-42EE-B94F-5AF431D86354}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.TestSample01", "src\samples\WorkflowCore.TestSample01\WorkflowCore.TestSample01.csproj", "{0E3C1496-8E7C-411A-A536-C7C9CE4EED4E}" EndProject diff --git a/test/WorkflowCore.Testing/JsonWorkflowTest.cs b/src/WorkflowCore.Testing/JsonWorkflowTest.cs similarity index 100% rename from test/WorkflowCore.Testing/JsonWorkflowTest.cs rename to src/WorkflowCore.Testing/JsonWorkflowTest.cs diff --git a/test/WorkflowCore.Testing/WorkflowCore.Testing.csproj b/src/WorkflowCore.Testing/WorkflowCore.Testing.csproj similarity index 100% rename from test/WorkflowCore.Testing/WorkflowCore.Testing.csproj rename to src/WorkflowCore.Testing/WorkflowCore.Testing.csproj diff --git a/test/WorkflowCore.Testing/WorkflowTest.cs b/src/WorkflowCore.Testing/WorkflowTest.cs similarity index 100% rename from test/WorkflowCore.Testing/WorkflowTest.cs rename to src/WorkflowCore.Testing/WorkflowTest.cs diff --git a/test/WorkflowCore.Testing/YamlWorkflowTest.cs b/src/WorkflowCore.Testing/YamlWorkflowTest.cs similarity index 100% rename from test/WorkflowCore.Testing/YamlWorkflowTest.cs rename to src/WorkflowCore.Testing/YamlWorkflowTest.cs diff --git a/test/WorkflowCore.Testing/readme.md b/src/WorkflowCore.Testing/readme.md similarity index 100% rename from test/WorkflowCore.Testing/readme.md rename to src/WorkflowCore.Testing/readme.md diff --git a/src/samples/Directory.Build.props b/src/samples/Directory.Build.props new file mode 100644 index 000000000..0f8bd594d --- /dev/null +++ b/src/samples/Directory.Build.props @@ -0,0 +1,7 @@ + + + net6.0;netcoreapp3.1 + latest + false + + \ No newline at end of file diff --git a/src/samples/WebApiSample/Directory.Build.props b/src/samples/WebApiSample/Directory.Build.props new file mode 100644 index 000000000..b312f7757 --- /dev/null +++ b/src/samples/WebApiSample/Directory.Build.props @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj b/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj index e5545be0c..3290c0dbf 100644 --- a/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj +++ b/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj @@ -1,7 +1,6 @@  - netcoreapp2.1 Linux ..\docker-compose.dcproj diff --git a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj index e4a09e491..d20a6bc51 100644 --- a/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj +++ b/src/samples/WorkflowCore.Sample01/WorkflowCore.Sample01.csproj @@ -1,7 +1,6 @@  - netcoreapp2.2 WorkflowCore.Sample01 Exe WorkflowCore.Sample01 diff --git a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj index 0765d50c3..be4e85c2d 100644 --- a/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj +++ b/src/samples/WorkflowCore.Sample02/WorkflowCore.Sample02.csproj @@ -1,7 +1,6 @@  - netcoreapp2.2 WorkflowCore.Sample02 Exe WorkflowCore.Sample02 diff --git a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj index d211d3d2f..72c15f156 100644 --- a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj +++ b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj @@ -1,7 +1,6 @@  - netcoreapp3.1 WorkflowCore.Sample03 Exe WorkflowCore.Sample03 diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index ddae958be..36bc3c4d3 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -1,7 +1,6 @@  - netcoreapp3.1 WorkflowCore.Sample04 Exe WorkflowCore.Sample04 diff --git a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj index 592fa228b..bbfd7f7c7 100644 --- a/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj +++ b/src/samples/WorkflowCore.Sample05/WorkflowCore.Sample05.csproj @@ -1,7 +1,6 @@  - netcoreapp2.0 WorkflowCore.Sample05 Exe WorkflowCore.Sample05 diff --git a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj index b61478b9e..47e03b0ee 100644 --- a/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj +++ b/src/samples/WorkflowCore.Sample06/WorkflowCore.Sample06.csproj @@ -1,7 +1,6 @@  - netcoreapp2.0 WorkflowCore.Sample06 Exe WorkflowCore.Sample06 diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index 2f5937f36..89096e798 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -1,7 +1,6 @@  - netcoreapp3.1 true WorkflowCore.Sample07 Exe diff --git a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj index 3ff429e28..f6ae3bcba 100644 --- a/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj +++ b/src/samples/WorkflowCore.Sample08/WorkflowCore.Sample08.csproj @@ -1,7 +1,6 @@  - netcoreapp3.0 WorkflowCore.Sample08 Exe WorkflowCore.Sample08 diff --git a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj index 6f75a0d7d..e7c7206ec 100644 --- a/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj +++ b/src/samples/WorkflowCore.Sample09/WorkflowCore.Sample09.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp3.0 diff --git a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj index e569ec895..67c94bc64 100644 --- a/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj +++ b/src/samples/WorkflowCore.Sample09s/WorkflowCore.Sample09s.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp3.0 diff --git a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj index 6f75a0d7d..e7c7206ec 100644 --- a/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj +++ b/src/samples/WorkflowCore.Sample10/WorkflowCore.Sample10.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp3.0 diff --git a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj index b6454f6a2..106048445 100644 --- a/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj +++ b/src/samples/WorkflowCore.Sample11/WorkflowCore.Sample11.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp2.2 diff --git a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj index 9879baab2..e0fb0e8aa 100644 --- a/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj +++ b/src/samples/WorkflowCore.Sample12/WorkflowCore.Sample12.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp3.0 diff --git a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj index 34e1b0433..522eaac98 100644 --- a/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj +++ b/src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp3.1 diff --git a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj index 974472a25..539973bee 100644 --- a/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj +++ b/src/samples/WorkflowCore.Sample14/WorkflowCore.Sample14.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp3.1 diff --git a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj index c63fbd792..f8d1df555 100644 --- a/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj +++ b/src/samples/WorkflowCore.Sample15/WorkflowCore.Sample15.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp2.2 diff --git a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj index c63fbd792..f8d1df555 100644 --- a/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj +++ b/src/samples/WorkflowCore.Sample16/WorkflowCore.Sample16.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp2.2 diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index 766fc8e0c..aa4cb91d8 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp3.1 diff --git a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj index 660b47511..c1c6e0846 100644 --- a/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj +++ b/src/samples/WorkflowCore.Sample18/WorkflowCore.Sample18.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp3.0 diff --git a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj index b2a7796c3..181b45e66 100644 --- a/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj +++ b/src/samples/WorkflowCore.Sample19/WorkflowCore.Sample19.csproj @@ -2,7 +2,6 @@ Exe - netcoreapp3.0 diff --git a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj index 46df42954..308565745 100644 --- a/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj +++ b/src/samples/WorkflowCore.TestSample01/WorkflowCore.TestSample01.csproj @@ -1,9 +1,5 @@  - - netcoreapp3.1 - - @@ -15,7 +11,7 @@ - + diff --git a/test/Directory.Build.props b/test/Directory.Build.props new file mode 100644 index 000000000..0f8bd594d --- /dev/null +++ b/test/Directory.Build.props @@ -0,0 +1,7 @@ + + + net6.0;netcoreapp3.1 + latest + false + + \ No newline at end of file diff --git a/test/Docker.Testify/Docker.Testify.csproj b/test/Docker.Testify/Docker.Testify.csproj index 8d4989b7c..a83648df4 100644 --- a/test/Docker.Testify/Docker.Testify.csproj +++ b/test/Docker.Testify/Docker.Testify.csproj @@ -1,9 +1,5 @@  - - netstandard2.0 - - diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index 2b63861b1..fdd4f1a3d 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -1,7 +1,6 @@  - netcoreapp3.1 ScratchPad Exe ScratchPad diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index f52ee0ba1..39390c2e6 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -1,7 +1,6 @@  - netcoreapp3.1 WorkflowCore.IntegrationTests WorkflowCore.IntegrationTests true @@ -14,7 +13,7 @@ - + diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index 0ff0a1fc2..73bd5b39d 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -1,7 +1,6 @@  - netstandard2.0 WorkflowCore.TestAssets WorkflowCore.TestAssets false diff --git a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index f275a05cf..b7d5a57b4 100644 --- a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -1,11 +1,5 @@ - - netcoreapp3.1 - - false - - diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 60e631bd7..887e2df63 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -1,11 +1,5 @@  - - netcoreapp3.1 - - false - - diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index a76f2b513..98fdd4ce8 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -1,7 +1,6 @@  - netcoreapp3.1 WorkflowCore.Tests.MongoDB WorkflowCore.Tests.MongoDB true @@ -15,7 +14,7 @@ - + diff --git a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj index ff53c72b2..1432348e2 100644 --- a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj +++ b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj @@ -1,11 +1,5 @@ - - netcoreapp3.1 - - false - - @@ -18,7 +12,7 @@ - + diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 2fefbdea8..64b475b13 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -1,7 +1,6 @@  - netcoreapp3.1 WorkflowCore.Tests.PostgreSQL WorkflowCore.Tests.PostgreSQL true @@ -16,7 +15,7 @@ - + diff --git a/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj b/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj index 7ccd35c02..0173d76bd 100644 --- a/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj +++ b/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj @@ -1,11 +1,5 @@ - - netcoreapp3.1 - - false - - diff --git a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj index af4f4117d..0ee3705bf 100644 --- a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj +++ b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj @@ -1,11 +1,5 @@ - - netcoreapp3.1 - - false - - diff --git a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj index 497293aaf..36ee2a15d 100644 --- a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj +++ b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj @@ -1,9 +1,5 @@  - - netcoreapp3.1 - - diff --git a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj index 9598398f4..31f839cf3 100644 --- a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj +++ b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj @@ -1,9 +1,5 @@  - - netcoreapp3.1 - - diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index 81ceeb3b1..f6ae01fbf 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -1,7 +1,6 @@  - netcoreapp3.1 WorkflowCore.UnitTests WorkflowCore.UnitTests true From 3c3b1ea47f655edb08bf7bbea3c9d35386506309 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 28 Mar 2022 10:43:24 +0200 Subject: [PATCH 398/462] Update build to .NET 6 --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 8ede77162..72d9e42ac 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.1.* + dotnet-version: 6.0.* - name: Restore dependencies run: dotnet restore - name: Build From a9e5012229d6ade7916bed9b6d142728ac1a5e84 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 28 Mar 2022 10:45:59 +0200 Subject: [PATCH 399/462] Add .netcoreapp 3.1 dependencie for tests --- .github/workflows/dotnet.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 72d9e42ac..9a49e0237 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,7 +16,9 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 6.0.* + dotnet-version: | + 3.1.x + 6.0.x - name: Restore dependencies run: dotnet restore - name: Build From c166817fdcd580874794b34fc7f12cd6488cce2f Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 29 Mar 2022 17:51:02 +0200 Subject: [PATCH 400/462] Update packages for .NET6.0 --- ...orkflowCore.Persistence.EntityFramework.csproj | 11 +++++++++-- .../WorkflowCore.Persistence.MySQL.csproj | 10 +++++++++- .../WorkflowCore.Persistence.PostgreSQL.csproj | 15 +++++++++++++-- .../WorkflowCore.Persistence.SqlServer.csproj | 15 +++++++++++++-- .../WorkflowCore.Persistence.Sqlite.csproj | 8 ++++++-- .../WorkflowCore.Sample03.csproj | 1 - .../WorkflowCore.Sample04.csproj | 14 -------------- .../WorkflowCore.Sample07.csproj | 1 - .../WorkflowCore.Sample17.csproj | 4 ---- .../WorkflowCore.Tests.PostgreSQL.csproj | 1 - 10 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 6e1206632..bb07fffd3 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -3,7 +3,7 @@ Workflow Core EntityFramework Core Persistence Provider Daniel Gerlag - netstandard2.1 + netstandard2.1;net6.0 WorkflowCore.Persistence.EntityFramework WorkflowCore.Persistence.EntityFramework workflow;.NET;Core;state machine;WorkflowCore;EntityFramework;EntityFrameworkCore @@ -21,9 +21,16 @@ - + + + + + + + + diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index 1b2fa9f6b..aaf1f79b0 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -4,7 +4,7 @@ Workflow Core MySQL Persistence Provider 1.0.0 Daniel Gerlag - netstandard2.1 + netstandard2.1;net6.0 WorkflowCore.Persistence.MySQL WorkflowCore.Persistence.MySQL workflow;.NET;Core;state machine;WorkflowCore;MySQL @@ -34,6 +34,14 @@ + + + all + runtime; build; native; contentfiles; analyzers + + + + diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 3aa405620..5ff53cd02 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -3,7 +3,7 @@ Workflow Core PostgreSQL Persistence Provider Daniel Gerlag - netstandard2.1 + netstandard2.1;net6.0 WorkflowCore.Persistence.PostgreSQL WorkflowCore.Persistence.PostgreSQL workflow;.NET;Core;state machine;WorkflowCore;PostgreSQL @@ -22,7 +22,18 @@ - + + + + All + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index 38375e8e6..be099421a 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -4,7 +4,7 @@ Workflow Core SQL Server Persistence Provider 1.8.0 Daniel Gerlag - netstandard2.1 + netstandard2.1;net6.0 WorkflowCore.Persistence.SqlServer WorkflowCore.Persistence.SqlServer workflow;.NET;Core;state machine;WorkflowCore @@ -23,7 +23,18 @@ - + + + + All + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + All diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index d4c73e037..0bd622bde 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -4,7 +4,7 @@ Workflow Core Sqlite Persistence Provider 1.5.0 Daniel Gerlag - netstandard2.1 + netstandard2.1;net6.0 WorkflowCore.Persistence.Sqlite WorkflowCore.Persistence.Sqlite workflow;.NET;Core;state machine;WorkflowCore;Sqlite @@ -23,7 +23,11 @@ - + + + + + diff --git a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj index 72c15f156..180c03454 100644 --- a/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj +++ b/src/samples/WorkflowCore.Sample03/WorkflowCore.Sample03.csproj @@ -15,7 +15,6 @@ - diff --git a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj index 6e78c2961..5940a8e00 100644 --- a/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj +++ b/src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj @@ -24,20 +24,6 @@ - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - diff --git a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj index 89096e798..1cc78f66f 100644 --- a/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj +++ b/src/samples/WorkflowCore.Sample07/WorkflowCore.Sample07.csproj @@ -25,7 +25,6 @@ - diff --git a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj index aa4cb91d8..f54a7ad6d 100644 --- a/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj +++ b/src/samples/WorkflowCore.Sample17/WorkflowCore.Sample17.csproj @@ -4,10 +4,6 @@ Exe - - - - diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 64b475b13..38cdefaba 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -20,7 +20,6 @@ - From 70e510421b6d69d4d37eacd04404ac06ad4f9cb2 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 29 Mar 2022 09:02:11 -0700 Subject: [PATCH 401/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 29d5fdae3..b32b80bf4 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.6.2 - 3.6.2.0 - 3.6.2.0 + 3.6.3 + 3.6.3.0 + 3.6.3.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.6.2 + 3.6.3 From b0d67558e2879848a211612b2abd085cdf0c4d2f Mon Sep 17 00:00:00 2001 From: Luis Fernando Bertucci Date: Wed, 22 Jun 2022 11:22:12 -0300 Subject: [PATCH 402/462] Added 'ProceedOnCancel' to WorkflowCore.DSL during load definition on convert steps --- src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs | 5 +++-- src/WorkflowCore.DSL/Services/DefinitionLoader.cs | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs b/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs index fb67ba449..07480225a 100644 --- a/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs +++ b/src/WorkflowCore.DSL/Models/v1/StepSourceV1.cs @@ -7,7 +7,7 @@ namespace WorkflowCore.Models.DefinitionStorage.v1 public class StepSourceV1 { public string StepType { get; set; } - + public string Id { get; set; } public string Name { get; set; } @@ -29,8 +29,9 @@ public class StepSourceV1 public ExpandoObject Inputs { get; set; } = new ExpandoObject(); public Dictionary Outputs { get; set; } = new Dictionary(); - + public Dictionary SelectNextStep { get; set; } = new Dictionary(); + public bool ProceedOnCancel { get; set; } = false; } } diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index 90b830990..ef7fadc9a 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -101,6 +101,7 @@ private WorkflowStepCollection ConvertSteps(ICollection source, Ty targetStep.ErrorBehavior = nextStep.ErrorBehavior; targetStep.RetryInterval = nextStep.RetryInterval; targetStep.ExternalId = $"{nextStep.Id}"; + targetStep.ProceedOnCancel = nextStep.ProceedOnCancel; AttachInputs(nextStep, dataType, stepType, targetStep); AttachOutputs(nextStep, dataType, stepType, targetStep); From 3266b42a79a86edba62946491eaa3e0cd2be1fbe Mon Sep 17 00:00:00 2001 From: Luis Fernando Bertucci Date: Wed, 22 Jun 2022 12:48:36 -0300 Subject: [PATCH 403/462] Adjusts tests to cover the new WorkflowCore.DSL definition loader property 'ProceedOnCancel' --- .../Scenarios/StoredJsonScenario.cs | 6 +++++- .../Scenarios/StoredYamlScenario.cs | 1 + .../DataTypes/CounterBoard.cs | 1 + .../stored-definition.json | 20 +++++++++++++++++++ .../stored-definition.yaml | 15 ++++++++++++++ .../stored-dynamic-definition.json | 20 +++++++++++++++++++ .../stored-dynamic-definition.yaml | 15 ++++++++++++++ 7 files changed, 77 insertions(+), 1 deletion(-) diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs index cbd04a40a..383eb39a4 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredJsonScenario.cs @@ -31,6 +31,7 @@ public void should_execute_branch1() data.Counter6.Should().Be(1); data.Counter7.Should().Be(1); data.Counter8.Should().Be(0); + data.Counter10.Should().Be(1); } [Fact(DisplayName = "Execute branch 2")] @@ -50,6 +51,7 @@ public void should_execute_branch2() data.Counter6.Should().Be(1); data.Counter7.Should().Be(0); data.Counter8.Should().Be(1); + data.Counter10.Should().Be(1); } [Fact] @@ -64,7 +66,8 @@ public void should_execute_json_workflow_with_dynamic_data() ["Counter3"] = 0, ["Counter4"] = 0, ["Counter5"] = 0, - ["Counter6"] = 0 + ["Counter6"] = 0, + ["Counter10"] = 0 }; var workflowId = StartWorkflow(TestAssets.Utils.GetTestDefinitionDynamicJson(), initialData); @@ -79,6 +82,7 @@ public void should_execute_json_workflow_with_dynamic_data() data["Counter4"].Should().Be(1); data["Counter5"].Should().Be(0); data["Counter6"].Should().Be(1); + data["Counter10"].Should().Be(1); } } } diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs index 449a6c855..95243ff03 100644 --- a/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs +++ b/test/WorkflowCore.IntegrationTests/Scenarios/StoredYamlScenario.cs @@ -29,6 +29,7 @@ public void should_execute_yaml_workflow() data.Counter4.Should().Be(1); data.Counter5.Should().Be(0); data.Counter6.Should().Be(1); + data.Counter10.Should().Be(1); } } } diff --git a/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs b/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs index eec59ecd6..a93b43585 100644 --- a/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs +++ b/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs @@ -13,6 +13,7 @@ public class CounterBoard public int Counter7 { get; set; } public int Counter8 { get; set; } public int Counter9 { get; set; } + public int Counter10 { get; set; } public bool Flag1 { get; set; } public bool Flag2 { get; set; } public bool Flag3 { get; set; } diff --git a/test/WorkflowCore.TestAssets/stored-definition.json b/test/WorkflowCore.TestAssets/stored-definition.json index 1db3a9174..7c77806b3 100644 --- a/test/WorkflowCore.TestAssets/stored-definition.json +++ b/test/WorkflowCore.TestAssets/stored-definition.json @@ -58,6 +58,26 @@ "Inputs": { "Value": "data.Counter5" }, "Outputs": { "Counter5": "step.Value" } } + ], + [ + { + "Id": "Step3.3.1", + "StepType": "WorkflowCore.Primitives.WaitFor, WorkflowCore", + "NextStepId": "Step3.3.2", + "CancelCondition": "data.Flag2", + "ProceedOnCancel": true, + "Inputs": { + "EventName": "\"Event1\"", + "EventKey": "\"Key1\"", + "EffectiveDate": "DateTime.Now" + } + }, + { + "Id": "Step3.3.2", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "Inputs": { "Value": "data.Counter10" }, + "Outputs": { "Counter10": "step.Value" } + } ] ] }, diff --git a/test/WorkflowCore.TestAssets/stored-definition.yaml b/test/WorkflowCore.TestAssets/stored-definition.yaml index f01efb07b..e469b1e88 100644 --- a/test/WorkflowCore.TestAssets/stored-definition.yaml +++ b/test/WorkflowCore.TestAssets/stored-definition.yaml @@ -50,6 +50,21 @@ Steps: Value: data.Counter5 Outputs: Counter5: step.Value + - - Id: Step3.3.1 + StepType: WorkflowCore.Primitives.WaitFor, WorkflowCore + NextStepId: Step3.3.2 + CancelCondition: data.Flag2 + ProceedOnCancel: true + Inputs: + EventName: '"Event1"' + EventKey: '"Key1"' + EffectiveDate: DateTime.Now + - Id: Step3.3.2 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter10 + Outputs: + Counter10: step.Value - Id: Step4 StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets Inputs: diff --git a/test/WorkflowCore.TestAssets/stored-dynamic-definition.json b/test/WorkflowCore.TestAssets/stored-dynamic-definition.json index dc572f8af..8c6a10dbe 100644 --- a/test/WorkflowCore.TestAssets/stored-dynamic-definition.json +++ b/test/WorkflowCore.TestAssets/stored-dynamic-definition.json @@ -57,6 +57,26 @@ "Inputs": { "Value": "data[\"Counter5\"]" }, "Outputs": { "Counter5": "step.Value" } } + ], + [ + { + "Id": "Step3.3.1", + "StepType": "WorkflowCore.Primitives.WaitFor, WorkflowCore", + "NextStepId": "Step3.3.2", + "CancelCondition": "object.Equals(data[\"Flag2\"], true)", + "ProceedOnCancel": true, + "Inputs": { + "EventName": "\"Event1\"", + "EventKey": "\"Key1\"", + "EffectiveDate": "DateTime.Now" + } + }, + { + "Id": "Step3.3.2", + "StepType": "WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets", + "Inputs": { "Value": "data[\"Counter10\"]" }, + "Outputs": { "Counter10": "step.Value" } + } ] ] }, diff --git a/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml b/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml index 6c38cd67f..9888be21e 100644 --- a/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml +++ b/test/WorkflowCore.TestAssets/stored-dynamic-definition.yaml @@ -50,6 +50,21 @@ Steps: Value: data.Counter5 Outputs: Counter5: step.Value + - - Id: Step3.3.1 + StepType: WorkflowCore.Primitives.WaitFor, WorkflowCore + NextStepId: Step3.3.2 + CancelCondition: data.Flag2 + ProceedOnCancel: true + Inputs: + EventName: '"Event1"' + EventKey: '"Key1"' + EffectiveDate: DateTime.Now + - Id: Step3.3.2 + StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets + Inputs: + Value: data.Counter10 + Outputs: + Counter10: step.Value - Id: Step4 StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets Inputs: From 6d77790dc1053aeb6f876d466f97c41e8c878ca8 Mon Sep 17 00:00:00 2001 From: Luis Fernando Bertucci Date: Wed, 22 Jun 2022 13:20:07 -0300 Subject: [PATCH 404/462] Changed to consider .NET Core in MySql provider --- src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs index dc49617fd..2b65f640d 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs +++ b/src/providers/WorkflowCore.Persistence.MySQL/MysqlContext.cs @@ -23,7 +23,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) base.OnConfiguring(optionsBuilder); #if NETSTANDARD2_0 optionsBuilder.UseMySql(_connectionString, _mysqlOptionsAction); -#elif NETSTANDARD2_1_OR_GREATER +#elif NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER optionsBuilder.UseMySql(_connectionString, ServerVersion.AutoDetect(_connectionString), _mysqlOptionsAction); #endif } From b4473806372dcc8ff1038e25c47caa8f0a85268a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jun 2022 20:35:00 +0000 Subject: [PATCH 405/462] Bump Newtonsoft.Json in /src/providers/WorkflowCore.Providers.AWS Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.1 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.1...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/WorkflowCore/WorkflowCore.csproj | 2 +- .../WorkflowCore.Providers.AWS.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 0a238b8aa..bb567d87c 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj index 5600452a2..4ea4a1ed8 100644 --- a/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj +++ b/src/providers/WorkflowCore.Providers.AWS/WorkflowCore.Providers.AWS.csproj @@ -18,7 +18,7 @@ - + From 69a37c2416dc20e308c6e9444e2db2613480115e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jun 2022 23:27:49 +0000 Subject: [PATCH 406/462] Bump Newtonsoft.Json from 12.0.3 to 13.0.1 in /src/WorkflowCore.DSL Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.3 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.3...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 2 +- src/WorkflowCore/WorkflowCore.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index 1a0130fc9..b3ce61cef 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/WorkflowCore/WorkflowCore.csproj b/src/WorkflowCore/WorkflowCore.csproj index 0a238b8aa..bb567d87c 100644 --- a/src/WorkflowCore/WorkflowCore.csproj +++ b/src/WorkflowCore/WorkflowCore.csproj @@ -19,7 +19,7 @@ - + From b276caa1c5995af800d849119604fdb05c5c3461 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jun 2022 02:03:05 +0000 Subject: [PATCH 407/462] Bump Newtonsoft.Json in /src/samples/WebApiSample/WebApiSample Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.1 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.1...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/samples/WebApiSample/WebApiSample/WebApiSample.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj b/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj index 3290c0dbf..133f705b4 100644 --- a/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj +++ b/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj @@ -14,7 +14,7 @@ - + From da0c23a9febcf4660bd46f22d1cfcead8d9c478b Mon Sep 17 00:00:00 2001 From: Eugene Andrukh Date: Thu, 23 Jun 2022 17:06:02 +0300 Subject: [PATCH 408/462] Do not push duplicates to Redis queue, simulate unique list behavior --- .../Services/RedisQueueProvider.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs index 460f3d741..347c699b6 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisQueueProvider.cs @@ -36,7 +36,13 @@ public async Task QueueWork(string id, QueueType queue) if (_redis == null) throw new InvalidOperationException(); - await _redis.ListRightPushAsync(GetQueueName(queue), id, When.Always); + var queueName = GetQueueName(queue); + + var insertResult = await _redis.ListInsertBeforeAsync(queueName, id, id); + if (insertResult == -1 || insertResult == 0) + await _redis.ListRightPushAsync(queueName, id, When.Always); + else + await _redis.ListRemoveAsync(queueName, id, 1); } public async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) From bb63980f804c83f9a04f9e2ca999f42bd39c57f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jun 2022 18:37:43 +0000 Subject: [PATCH 409/462] Bump Newtonsoft.Json Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.1 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.1...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../WorkflowCore.Persistence.EntityFramework.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index bb07fffd3..6ab835915 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -31,7 +31,7 @@ - + From 2be0e5d2075c2164c0a60bd64f3b6e05d0f0a3bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jun 2022 18:37:58 +0000 Subject: [PATCH 410/462] Bump Newtonsoft.Json in /src/providers/WorkflowCore.Persistence.MongoDB Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.1 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.1...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../WorkflowCore.Persistence.MongoDB.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 13df8d4aa..5d395eee9 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -23,7 +23,7 @@ - + From 4864e0ac3db9d99369e8061f711110e35a0e66d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jun 2022 18:38:10 +0000 Subject: [PATCH 411/462] Bump Newtonsoft.Json in /test/WorkflowCore.TestAssets Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.1 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.1...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index 73bd5b39d..4530229d1 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -40,7 +40,7 @@ - + From 0cd378da79366d994c163f51f1d1353d1c79425c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jun 2022 18:38:11 +0000 Subject: [PATCH 412/462] Bump Newtonsoft.Json Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.1 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.1...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../WorkflowCore.QueueProviders.RabbitMQ.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj index 48041387f..3ef1064ae 100644 --- a/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj +++ b/src/providers/WorkflowCore.QueueProviders.RabbitMQ/WorkflowCore.QueueProviders.RabbitMQ.csproj @@ -22,7 +22,7 @@ - + From 8eb3e2792412d8e2824c05c9235caf5bc08895aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jun 2022 18:38:40 +0000 Subject: [PATCH 413/462] Bump Newtonsoft.Json in /src/providers/WorkflowCore.Providers.Redis Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.1 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.1...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../WorkflowCore.Providers.Redis.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj index e35b091ff..b1cc0aa97 100644 --- a/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj +++ b/src/providers/WorkflowCore.Providers.Redis/WorkflowCore.Providers.Redis.csproj @@ -11,7 +11,7 @@ - + From 6840d90b7b9d07cdf4faf54c6995961e3f534157 Mon Sep 17 00:00:00 2001 From: Eugene Andrukh Date: Thu, 23 Jun 2022 17:34:41 +0300 Subject: [PATCH 414/462] Add prefix to RedisLockProvider #1044 --- .../ServiceCollectionExtensions.cs | 4 ++-- .../Services/RedisLockProvider.cs | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs index 514ba21a3..27594cfac 100644 --- a/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.Redis/ServiceCollectionExtensions.cs @@ -13,9 +13,9 @@ public static WorkflowOptions UseRedisQueues(this WorkflowOptions options, strin return options; } - public static WorkflowOptions UseRedisLocking(this WorkflowOptions options, string connectionString) + public static WorkflowOptions UseRedisLocking(this WorkflowOptions options, string connectionString, string prefix = null) { - options.UseDistributedLockManager(sp => new RedisLockProvider(connectionString, sp.GetService())); + options.UseDistributedLockManager(sp => new RedisLockProvider(connectionString, prefix, sp.GetService())); return options; } diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs index 469ca2763..f71a1c323 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisLockProvider.cs @@ -14,15 +14,17 @@ namespace WorkflowCore.Providers.Redis.Services public class RedisLockProvider : IDistributedLockProvider { private readonly ILogger _logger; - private readonly string _connectionString; + private readonly string _connectionString; + private readonly string _prefix; private IConnectionMultiplexer _multiplexer; private RedLockFactory _redlockFactory; private readonly TimeSpan _lockTimeout = TimeSpan.FromMinutes(1); private readonly List ManagedLocks = new List(); - public RedisLockProvider(string connectionString, ILoggerFactory logFactory) + public RedisLockProvider(string connectionString, string prefix, ILoggerFactory logFactory) { _connectionString = connectionString; + _prefix = prefix; _logger = logFactory.CreateLogger(GetType()); } @@ -31,7 +33,7 @@ public async Task AcquireLock(string Id, CancellationToken cancellationTok if (_redlockFactory == null) throw new InvalidOperationException(); - var redLock = await _redlockFactory.CreateLockAsync(Id, _lockTimeout); + var redLock = await _redlockFactory.CreateLockAsync(GetResource(Id), _lockTimeout); if (redLock.IsAcquired) { @@ -50,11 +52,13 @@ public Task ReleaseLock(string Id) if (_redlockFactory == null) throw new InvalidOperationException(); + var resource = GetResource(Id); + lock (ManagedLocks) { foreach (var redLock in ManagedLocks) { - if (redLock.Resource == Id) + if (redLock.Resource == resource) { redLock.Dispose(); ManagedLocks.Remove(redLock); @@ -77,7 +81,15 @@ public async Task Stop() _redlockFactory?.Dispose(); await _multiplexer.CloseAsync(); _multiplexer = null; - + + } + + private string GetResource(string key) + { + if (string.IsNullOrEmpty(_prefix)) + return key; + + return $"{_prefix}:{key}"; } } } From 8fa1fb7c8f566aadce67535f26fb37268660522b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 5 Jul 2022 14:39:14 -0700 Subject: [PATCH 415/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index b32b80bf4..30816a3b6 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.6.3 - 3.6.3.0 - 3.6.3.0 + 3.6.4 + 3.6.4.0 + 3.6.4.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.6.3 + 3.6.4 From 60cfe3dae2bba6ea656e19b64ec63088e1af675a Mon Sep 17 00:00:00 2001 From: Pavel Gorbenko Date: Tue, 26 Jul 2022 17:36:46 +0400 Subject: [PATCH 416/462] the names of the fields in the logs have been changed --- .../Services/BackgroundTasks/EventConsumer.cs | 4 ++-- .../Services/BackgroundTasks/RunnablePoller.cs | 4 ++-- .../Services/BackgroundTasks/WorkflowConsumer.cs | 6 +++--- src/WorkflowCore/Services/WorkflowController.cs | 4 ++-- src/WorkflowCore/Services/WorkflowExecutor.cs | 12 ++++++------ 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs index dd7323b01..26e630ea5 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/EventConsumer.cs @@ -114,7 +114,7 @@ private async Task SeedSubscription(Event evt, EventSubscription sub, Hash if (!await _lockProvider.AcquireLock(sub.WorkflowId, cancellationToken)) { - Logger.LogInformation("Workflow locked {0}", sub.WorkflowId); + Logger.LogInformation("Workflow locked {WorkflowId}", sub.WorkflowId); return false; } @@ -151,4 +151,4 @@ private async Task SeedSubscription(Event evt, EventSubscription sub, Hash } } } -} +} \ No newline at end of file diff --git a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs index fcd2abd92..29b76837c 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/RunnablePoller.cs @@ -94,7 +94,7 @@ await _persistenceStore.ScheduleCommand(new ScheduledCommand() _logger.LogDebug($"Got greylisted workflow {item}"); continue; } - _logger.LogDebug("Got runnable instance {0}", item); + _logger.LogDebug("Got runnable instance {Item}", item); _greylist.Add($"wf:{item}"); await _queueProvider.QueueWork(item, QueueType.Workflow); } @@ -218,4 +218,4 @@ await _persistenceStore.ProcessCommands(new DateTimeOffset(_dateTimeProvider.Utc } } } -} +} \ No newline at end of file diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index b3dd4aa0b..2d8cf257c 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -33,7 +33,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { if (!await _lockProvider.AcquireLock(itemId, cancellationToken)) { - Logger.LogInformation("Workflow locked {0}", itemId); + Logger.LogInformation("Workflow locked {ItemId}", itemId); return; } @@ -101,7 +101,7 @@ await _persistenceStore.ScheduleCommand(new ScheduledCommand() private async Task SubscribeEvent(EventSubscription subscription, IPersistenceProvider persistenceStore, CancellationToken cancellationToken) { //TODO: move to own class - Logger.LogDebug("Subscribing to event {0} {1} for workflow {2} step {3}", subscription.EventName, subscription.EventKey, subscription.WorkflowId, subscription.StepId); + Logger.LogDebug("Subscribing to event {EventName} {EventKey} for workflow {WorkflowId} step {StepId}", subscription.EventName, subscription.EventKey, subscription.WorkflowId, subscription.StepId); await persistenceStore.CreateEventSubscription(subscription, cancellationToken); if (subscription.EventName != Event.EventTypeActivity) @@ -169,4 +169,4 @@ private async void FutureQueue(WorkflowInstance workflow, CancellationToken canc } } } -} +} \ No newline at end of file diff --git a/src/WorkflowCore/Services/WorkflowController.cs b/src/WorkflowCore/Services/WorkflowController.cs index 6edb63aa7..79272e084 100755 --- a/src/WorkflowCore/Services/WorkflowController.cs +++ b/src/WorkflowCore/Services/WorkflowController.cs @@ -107,7 +107,7 @@ await _eventHub.PublishNotification(new WorkflowStarted public async Task PublishEvent(string eventName, string eventKey, object eventData, DateTime? effectiveDate = null) { - _logger.LogDebug("Creating event {0} {1}", eventName, eventKey); + _logger.LogDebug("Creating event {EventName} {EventKey}", eventName, eventKey); Event evt = new Event(); if (effectiveDate.HasValue) @@ -241,4 +241,4 @@ public void RegisterWorkflow() _registry.RegisterWorkflow(wf); } } -} +} \ No newline at end of file diff --git a/src/WorkflowCore/Services/WorkflowExecutor.cs b/src/WorkflowCore/Services/WorkflowExecutor.cs index 145f02d41..da3e9cd85 100755 --- a/src/WorkflowCore/Services/WorkflowExecutor.cs +++ b/src/WorkflowCore/Services/WorkflowExecutor.cs @@ -47,7 +47,7 @@ public async Task Execute(WorkflowInstance workflow, Can var def = _registry.GetDefinition(workflow.WorkflowDefinitionId, workflow.Version); if (def == null) { - _logger.LogError("Workflow {0} version {1} is not registered", workflow.WorkflowDefinitionId, workflow.Version); + _logger.LogError("Workflow {WorkflowDefinitionId} version {Version} is not registered", workflow.WorkflowDefinitionId, workflow.Version); return wfResult; } @@ -61,7 +61,7 @@ public async Task Execute(WorkflowInstance workflow, Can var step = def.Steps.FindById(pointer.StepId); if (step == null) { - _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); + _logger.LogError("Unable to find step {StepId} in workflow definition", pointer.StepId); pointer.SleepUntil = _datetimeProvider.UtcNow.Add(_options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError { @@ -83,7 +83,7 @@ public async Task Execute(WorkflowInstance workflow, Can } catch (Exception ex) { - _logger.LogError(ex, "Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); + _logger.LogError(ex, "Workflow {WorkflowId} raised error on step {StepId} Message: {Message}", workflow.Id, pointer.StepId, ex.Message); wfResult.Errors.Add(new ExecutionError { WorkflowId = workflow.Id, @@ -158,14 +158,14 @@ private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, Exe using (var scope = _scopeProvider.CreateScope(context)) { - _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); + _logger.LogDebug("Starting step {StepName} on workflow {WorkflowId}", step.Name, workflow.Id); IStepBody body = step.ConstructBody(scope.ServiceProvider); var stepExecutor = scope.ServiceProvider.GetRequiredService(); if (body == null) { - _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); + _logger.LogError("Unable to construct step body {BodyType}", step.BodyType.ToString()); pointer.SleepUntil = _datetimeProvider.UtcNow.Add(_options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError { @@ -275,4 +275,4 @@ private async Task DetermineNextExecutionTime(WorkflowInstance workflow, Workflo }); } } -} +} \ No newline at end of file From 4eb025c00bdb531bc98768759e59c8a32e67193f Mon Sep 17 00:00:00 2001 From: badihi Date: Mon, 1 Aug 2022 12:45:02 +0430 Subject: [PATCH 417/462] Fixes a bug that occures when a compensation is nested in a branch --- .../FluentBuilders/WorkflowBuilder.cs | 10 +++ .../ForeachWithCompensationScenario.cs | 69 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 test/WorkflowCore.IntegrationTests/Scenarios/ForeachWithCompensationScenario.cs diff --git a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs index 98788fa0c..967339967 100644 --- a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs @@ -84,6 +84,11 @@ public void AttachBranch(IWorkflowBuilder branch) if (step2.Children[i] == oldId) step2.Children[i] = step.Id; } + + if (step2.CompensationStepId == oldId) + { + step2.CompensationStepId = step.Id; + } } } @@ -104,6 +109,11 @@ public void AttachBranch(IWorkflowBuilder branch) if (step2.Children[i] == oldId) step2.Children[i] = step.Id; } + + if (step2.CompensationStepId == oldId) + { + step2.CompensationStepId = step.Id; + } } } diff --git a/test/WorkflowCore.IntegrationTests/Scenarios/ForeachWithCompensationScenario.cs b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachWithCompensationScenario.cs new file mode 100644 index 000000000..144774fcb --- /dev/null +++ b/test/WorkflowCore.IntegrationTests/Scenarios/ForeachWithCompensationScenario.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using Xunit; +using FluentAssertions; +using WorkflowCore.Testing; + +namespace WorkflowCore.IntegrationTests.Scenarios +{ + public class ForeachWithCompensationScenario : WorkflowTest + { + internal static int Step1Ticker = 0; + internal static int Step2Ticker = 0; + internal static int Step3Ticker = 0; + internal static int CompensateTicker = 0; + + public class MyDataClass + { + } + + public class ForeachWorkflow : IWorkflow + { + public string Id => "ForeachWithCompensationWorkflow"; + public int Version => 1; + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(data => { + Step1Ticker++; + }) + .Then(data => { + Step2Ticker++; + }) + .ForEach(step => new List { 1 }) + .Do(then => then + .Decide(data => 1) + .Branch(1, builder.CreateBranch() + .StartWith(data => { + Step3Ticker++; + throw new Exception(); + }) + .CompensateWithSequence(builder => builder.StartWith(_ => { + CompensateTicker++; + }))) + ); + } + } + + public ForeachWithCompensationScenario() + { + Setup(); + } + + [Fact] + public void Scenario() + { + var workflowId = StartWorkflow(new MyDataClass()); + WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30)); + + Step1Ticker.Should().Be(1); + Step2Ticker.Should().Be(1); + Step3Ticker.Should().Be(1); + CompensateTicker.Should().Be(1); + GetStatus(workflowId).Should().Be(WorkflowStatus.Complete); + UnhandledStepErrors.Count.Should().Be(1); + } + } +} From 202e7d36826bda9ee73092736c26cb38ffa9a7c0 Mon Sep 17 00:00:00 2001 From: Viktor Shevchenko Date: Fri, 5 Aug 2022 14:36:33 +0300 Subject: [PATCH 418/462] Fixed reliability of WorkflowConsumer when persisting workflow --- .../Persistence/IWorkflowRepository.cs | 2 + .../BackgroundTasks/WorkflowConsumer.cs | 6 +- .../MemoryPersistenceProvider.cs | 19 ++++++ .../TransientMemoryPersistenceProvider.cs | 10 +++ .../EntityFrameworkPersistenceProvider.cs | 26 ++++++++ .../Services/MongoPersistenceProvider.cs | 11 ++++ .../Services/RavendbPersistenceProvider.cs | 41 +++++++++--- .../Services/DynamoPersistenceProvider.cs | 37 +++++++++++ .../Services/CosmosDbPersistenceProvider.cs | 10 +++ .../Services/RedisPersistenceProvider.cs | 10 +++ .../BasePersistenceFixture.cs | 66 +++++++++++++++++++ 11 files changed, 223 insertions(+), 15 deletions(-) diff --git a/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs b/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs index 1da4275cd..09842af7a 100644 --- a/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs +++ b/src/WorkflowCore/Interface/Persistence/IWorkflowRepository.cs @@ -12,6 +12,8 @@ public interface IWorkflowRepository Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default); + Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default); + Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default); [Obsolete] diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index b3dd4aa0b..ce917e41a 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -55,7 +55,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance finally { WorkflowActivity.Enrich(result); - await _persistenceStore.PersistWorkflow(workflow, cancellationToken); + await _persistenceStore.PersistWorkflow(workflow, result.Subscriptions, cancellationToken); await QueueProvider.QueueWork(itemId, QueueType.Index); _greylist.Remove($"wf:{itemId}"); } @@ -100,10 +100,6 @@ await _persistenceStore.ScheduleCommand(new ScheduledCommand() private async Task SubscribeEvent(EventSubscription subscription, IPersistenceProvider persistenceStore, CancellationToken cancellationToken) { - //TODO: move to own class - Logger.LogDebug("Subscribing to event {0} {1} for workflow {2} step {3}", subscription.EventName, subscription.EventKey, subscription.WorkflowId, subscription.StepId); - - await persistenceStore.CreateEventSubscription(subscription, cancellationToken); if (subscription.EventName != Event.EventTypeActivity) { var events = await persistenceStore.GetEvents(subscription.EventName, subscription.EventKey, subscription.SubscribeAsOf, cancellationToken); diff --git a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs index e983f8fd5..cb69c8791 100644 --- a/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/MemoryPersistenceProvider.cs @@ -46,6 +46,25 @@ public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken _ } } + public async Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default) + { + lock (_instances) + { + var existing = _instances.First(x => x.Id == workflow.Id); + _instances.Remove(existing); + _instances.Add(workflow); + + lock (_subscriptions) + { + foreach (var subscription in subscriptions) + { + subscription.Id = Guid.NewGuid().ToString(); + _subscriptions.Add(subscription); + } + } + } + } + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken _ = default) { lock (_instances) diff --git a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs index 0a94c6f8e..31ec6dbe2 100644 --- a/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs +++ b/src/WorkflowCore/Services/DefaultProviders/TransientMemoryPersistenceProvider.cs @@ -50,6 +50,16 @@ public TransientMemoryPersistenceProvider(ISingletonMemoryProvider innerService) public Task PersistWorkflow(WorkflowInstance workflow, CancellationToken _ = default) => _innerService.PersistWorkflow(workflow); + public async Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default) + { + await PersistWorkflow(workflow, cancellationToken); + + foreach(var subscription in subscriptions) + { + await CreateEventSubscription(subscription, cancellationToken); + } + } + public Task TerminateSubscription(string eventSubscriptionId, CancellationToken _ = default) => _innerService.TerminateSubscription(eventSubscriptionId); public Task GetSubscription(string eventSubscriptionId, CancellationToken _ = default) => _innerService.GetSubscription(eventSubscriptionId); diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs index 44798a0c9..22ba680f5 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs @@ -151,6 +151,32 @@ public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken c await db.SaveChangesAsync(cancellationToken); } } + + public async Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default) + { + using (var db = ConstructDbContext()) + { + var uid = new Guid(workflow.Id); + var existingEntity = await db.Set() + .Where(x => x.InstanceId == uid) + .Include(wf => wf.ExecutionPointers) + .ThenInclude(ep => ep.ExtensionAttributes) + .Include(wf => wf.ExecutionPointers) + .AsTracking() + .FirstAsync(cancellationToken); + + var workflowPersistable = workflow.ToPersistable(existingEntity); + + foreach (var subscription in subscriptions) + { + subscription.Id = Guid.NewGuid().ToString(); + var subscriptionPersistable = subscription.ToPersistable(); + db.Set().Add(subscriptionPersistable); + } + + await db.SaveChangesAsync(cancellationToken); + } + } public async Task TerminateSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default) { diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 06be13355..2824f9674 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -149,6 +149,17 @@ public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken c await WorkflowInstances.ReplaceOneAsync(x => x.Id == workflow.Id, workflow, cancellationToken: cancellationToken); } + public async Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default) + { + using (var session = await _database.Client.StartSessionAsync()) + { + session.StartTransaction(); + await PersistWorkflow(workflow, cancellationToken); + await EventSubscriptions.InsertManyAsync(subscriptions, cancellationToken: cancellationToken); + await session.CommitTransactionAsync(); + } + } + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default) { var now = asAt.ToUniversalTime().Ticks; diff --git a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs index 695b8e31f..3e1d5e2ea 100644 --- a/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.RavenDB/Services/RavendbPersistenceProvider.cs @@ -1,6 +1,7 @@ using Raven.Client.Documents; using Raven.Client.Documents.Linq; using Raven.Client.Documents.Operations; +using Raven.Client.Documents.Session; using System; using System.Collections.Generic; using System.Linq; @@ -56,21 +57,41 @@ public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken c { using (var session = _database.OpenAsyncSession()) { - session.Advanced.Patch(workflow.Id, x => x.WorkflowDefinitionId, workflow.WorkflowDefinitionId); - session.Advanced.Patch(workflow.Id, x => x.Version, workflow.Version); - session.Advanced.Patch(workflow.Id, x => x.Description, workflow.Description); - session.Advanced.Patch(workflow.Id, x => x.Reference, workflow.Reference); - session.Advanced.Patch(workflow.Id, x => x.ExecutionPointers, workflow.ExecutionPointers); - session.Advanced.Patch(workflow.Id, x => x.NextExecution, workflow.NextExecution); - session.Advanced.Patch(workflow.Id, x => x.Status, workflow.Status); - session.Advanced.Patch(workflow.Id, x => x.Data, workflow.Data); - session.Advanced.Patch(workflow.Id, x => x.CreateTime, workflow.CreateTime); - session.Advanced.Patch(workflow.Id, x => x.CompleteTime, workflow.CompleteTime); + PatchSession(session, workflow); + await session.SaveChangesAsync(cancellationToken); + } + } + + public async Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default) + { + using (var session = _database.OpenAsyncSession()) + { + PatchSession(session, workflow); + + foreach (var subscription in subscriptions) + { + await session.StoreAsync(subscription, cancellationToken); + } await session.SaveChangesAsync(cancellationToken); } } + private void PatchSession(IAsyncDocumentSession session, WorkflowInstance workflow) + { + session.Advanced.Patch(workflow.Id, x => x.WorkflowDefinitionId, workflow.WorkflowDefinitionId); + session.Advanced.Patch(workflow.Id, x => x.Version, workflow.Version); + session.Advanced.Patch(workflow.Id, x => x.Description, workflow.Description); + session.Advanced.Patch(workflow.Id, x => x.Reference, workflow.Reference); + session.Advanced.Patch(workflow.Id, x => x.ExecutionPointers, workflow.ExecutionPointers); + session.Advanced.Patch(workflow.Id, x => x.NextExecution, workflow.NextExecution); + session.Advanced.Patch(workflow.Id, x => x.Status, workflow.Status); + session.Advanced.Patch(workflow.Id, x => x.Data, workflow.Data); + session.Advanced.Patch(workflow.Id, x => x.CreateTime, workflow.CreateTime); + session.Advanced.Patch(workflow.Id, x => x.CompleteTime, workflow.CompleteTime); + + } + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default) { var now = asAt.ToUniversalTime().Ticks; diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index 1126b6c20..09f1dbc4c 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -61,6 +61,43 @@ public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken c var response = await _client.PutItemAsync(request, cancellationToken); } + public async Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default) + { + var transactionWriteItemsRequest = new TransactWriteItemsRequest() + { + TransactItems = new List() + { + { + new TransactWriteItem() + { + Put = new Put() + { + TableName = $"{_tablePrefix}-{WORKFLOW_TABLE}", + Item = workflow.ToDynamoMap() + } + } + } + } + }; + + foreach(var subscription in subscriptions) + { + subscription.Id = Guid.NewGuid().ToString(); + + transactionWriteItemsRequest.TransactItems.Add(new TransactWriteItem() + { + Put = new Put() + { + TableName = $"{_tablePrefix}-{SUBCRIPTION_TABLE}", + Item = subscription.ToDynamoMap(), + ConditionExpression = "attribute_not_exists(id)" + } + }); + } + + await _client.TransactWriteItemsAsync(transactionWriteItemsRequest, cancellationToken); + } + public async Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default) { var result = new List(); diff --git a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs index 644009df2..008136494 100644 --- a/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Azure/Services/CosmosDbPersistenceProvider.cs @@ -227,6 +227,16 @@ public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken c await _workflowContainer.Value.UpsertItemAsync(PersistedWorkflow.FromInstance(workflow), cancellationToken: cancellationToken); } + public async Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default) + { + await PersistWorkflow(workflow, cancellationToken); + + foreach(var subscription in subscriptions) + { + await CreateEventSubscription(subscription, cancellationToken); + } + } + public Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default) { throw new NotImplementedException(); diff --git a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs index cde65e2e5..6bf8df875 100644 --- a/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.Redis/Services/RedisPersistenceProvider.cs @@ -47,6 +47,16 @@ public async Task CreateNewWorkflow(WorkflowInstance workflow, Cancellat return workflow.Id; } + public async Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default) + { + await PersistWorkflow(workflow, cancellationToken); + + foreach (var subscription in subscriptions) + { + await CreateEventSubscription(subscription, cancellationToken); + } + } + public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken _ = default) { var str = JsonConvert.SerializeObject(workflow, _serializerSettings); diff --git a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs index 0afa9bd7e..9114da7b9 100644 --- a/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs +++ b/test/WorkflowCore.UnitTests/BasePersistenceFixture.cs @@ -185,6 +185,72 @@ public void PersistWorkflow() var current = Subject.GetWorkflowInstance(workflowId).Result; current.ShouldBeEquivalentTo(newWorkflow); } + + [Fact] + public void PersistWorkflow_with_subscriptions() + { + var workflow = new WorkflowInstance + { + Data = new TestData { Value1 = 7 }, + Description = "My Description", + Status = WorkflowStatus.Runnable, + NextExecution = 0, + Version = 1, + WorkflowDefinitionId = "My Workflow", + CreateTime = new DateTime(2000, 1, 1).ToUniversalTime(), + ExecutionPointers = new ExecutionPointerCollection(), + Reference = Guid.NewGuid().ToString() + }; + + workflow.ExecutionPointers.Add(new ExecutionPointer + { + Id = Guid.NewGuid().ToString(), + Active = true, + StepId = 0, + Scope = new List { "1", "2", "3", "4" }, + EventName = "Event1" + }); + + workflow.ExecutionPointers.Add(new ExecutionPointer + { + Id = Guid.NewGuid().ToString(), + Active = true, + StepId = 1, + Scope = new List { "1", "2", "3", "4" }, + EventName = "Event2", + }); + + var workflowId = Subject.CreateNewWorkflow(workflow).Result; + workflow.NextExecution = 0; + + List subscriptions = new List(); + foreach (var pointer in workflow.ExecutionPointers) + { + var subscription = new EventSubscription() + { + WorkflowId = workflowId, + StepId = pointer.StepId, + ExecutionPointerId = pointer.Id, + EventName = pointer.EventName, + EventKey = workflowId, + SubscribeAsOf = DateTime.UtcNow, + SubscriptionData = "data" + }; + + subscriptions.Add(subscription); + } + + Subject.PersistWorkflow(workflow, subscriptions).Wait(); + + var current = Subject.GetWorkflowInstance(workflowId).Result; + current.ShouldBeEquivalentTo(workflow); + + foreach (var pointer in workflow.ExecutionPointers) + { + subscriptions = Subject.GetSubscriptions(pointer.EventName, workflowId, DateTime.UtcNow).Result.ToList(); + subscriptions.Should().HaveCount(1); + } + } [Fact] public void ConcurrentPersistWorkflow() From d0a2f74c4155db0f6e25f610462742e2776b690c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 13 Aug 2022 10:39:01 -0700 Subject: [PATCH 419/462] bump version --- src/Directory.Build.props | 8 +- ...WorkflowCore.Persistence.PostgreSQL.csproj | 5 +- .../Properties/Resources.Designer.cs | 186 +++++++++--------- .../Properties/Resources.Designer.cs | 2 +- 4 files changed, 101 insertions(+), 100 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 30816a3b6..bb20f98eb 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.6.4 - 3.6.4.0 - 3.6.4.0 + 3.6.5 + 3.6.5.0 + 3.6.5.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.6.4 + 3.6.5 diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index 5ff53cd02..e321a340a 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -23,7 +23,8 @@ - + + All @@ -34,7 +35,7 @@ - + All diff --git a/test/ScratchPad/Properties/Resources.Designer.cs b/test/ScratchPad/Properties/Resources.Designer.cs index bb7355e59..6e79d8a01 100644 --- a/test/ScratchPad/Properties/Resources.Designer.cs +++ b/test/ScratchPad/Properties/Resources.Designer.cs @@ -1,93 +1,93 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace ScratchPad.Properties { - using System; - using System.Reflection; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ScratchPad.Properties.Resources", typeof(Resources).GetTypeInfo().Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to { - /// "Id": "HelloWorld", - /// "Version": 1, - /// "Description": "", - /// "DataType": "System.Object", - /// "Steps": [ - /// { - /// "Id": "Hello", - /// "StepType": "ScratchPad.HelloWorld; ScratchPad", - /// "Name": "Hello", - /// "NextStepId": "Bye", - /// "Inputs": [], - /// "Outputs": [] - /// }, - /// { - /// "Id": "Bye", - /// "StepType": "ScratchPad.GoodbyeWorld; ScratchPad", - /// "Name": "Bye" - /// } - /// ] - ///}. - /// - internal static string HelloWorld { - get { - return ResourceManager.GetString("HelloWorld", resourceCulture); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ScratchPad.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ScratchPad.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to { + /// "Id": "Test02", + /// "Version": 1, + /// "Description": "", + /// "DataType": "ScratchPad.WfData, ScratchPad", + /// "Steps": [ + /// { + /// "Id": "Hello", + /// "StepType": "ScratchPad.HelloWorld, ScratchPad", + /// "NextStepId": "decide" + /// }, + /// { + /// "Id": "decide", + /// "StepType": "WorkflowCore.Primitives.Decide, WorkflowCore", + /// "SelectNextStep": + /// { + /// "Print1": "data.Value1 == \"one\"", + /// "Print2": "data.Value1 == \"two\"" + /// } + /// }, + /// { + /// "Id": "Print1", /// [rest of string was truncated]";. + /// + internal static string HelloWorld { + get { + return ResourceManager.GetString("HelloWorld", resourceCulture); + } + } + } +} diff --git a/test/WorkflowCore.TestAssets/Properties/Resources.Designer.cs b/test/WorkflowCore.TestAssets/Properties/Resources.Designer.cs index 286c37d49..d4b47397a 100644 --- a/test/WorkflowCore.TestAssets/Properties/Resources.Designer.cs +++ b/test/WorkflowCore.TestAssets/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace WorkflowCore.TestAssets.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resources { From 4cf59daf2d410f83f4caae668f21ab33943350a5 Mon Sep 17 00:00:00 2001 From: Viktor Shevchenko Date: Sun, 14 Aug 2022 15:00:02 +0300 Subject: [PATCH 420/462] Changed method name --- src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index ce917e41a..3a6465902 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -68,7 +68,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance { foreach (var sub in result.Subscriptions) { - await SubscribeEvent(sub, _persistenceStore, cancellationToken); + await TryProcessSubscription(sub, _persistenceStore, cancellationToken); } await _persistenceStore.PersistErrors(result.Errors, cancellationToken); @@ -98,7 +98,7 @@ await _persistenceStore.ScheduleCommand(new ScheduledCommand() } - private async Task SubscribeEvent(EventSubscription subscription, IPersistenceProvider persistenceStore, CancellationToken cancellationToken) + private async Task TryProcessSubscription(EventSubscription subscription, IPersistenceProvider persistenceStore, CancellationToken cancellationToken) { if (subscription.EventName != Event.EventTypeActivity) { From 7d3bbfa86361008839686904821b54bfc7e336db Mon Sep 17 00:00:00 2001 From: Martin Dennhardt Date: Wed, 24 Aug 2022 14:56:10 +0200 Subject: [PATCH 421/462] Make QueueConsumer.Stop() idempotent Fixes #1085 --- src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs index 1476e8b96..a5392ecbb 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/QueueConsumer.cs @@ -53,8 +53,11 @@ public virtual void Start() public virtual void Stop() { _cancellationTokenSource.Cancel(); - DispatchTask.Wait(); - DispatchTask = null; + if (DispatchTask != null) + { + DispatchTask.Wait(); + DispatchTask = null; + } } private async Task Execute() From a74727969e005dee6b4115ab4a18581b23d587b9 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 30 Aug 2022 15:03:33 -0700 Subject: [PATCH 422/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index bb20f98eb..298e3079c 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.6.5 - 3.6.5.0 - 3.6.5.0 + 3.7.0 + 3.7.0.0 + 3.7.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.6.5 + 3.7.0 From fc3c66ade8f55bc0f97e3bd329ccec19d398ebb8 Mon Sep 17 00:00:00 2001 From: Ivan Josipovic <9521987+IvanJosipovic@users.noreply.github.com> Date: Fri, 30 Sep 2022 16:54:09 -0700 Subject: [PATCH 423/462] feat: add IStepExecutionContext to UserTask --- .../StepBuilderExtensions.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs b/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs index 2b337c1fd..6f3f42935 100644 --- a/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs +++ b/src/extensions/WorkflowCore.Users/ServiceExtensions/StepBuilderExtensions.cs @@ -18,7 +18,7 @@ public static IStepBuilder UserStep(this ISte { var newStep = new UserStepContainer(); newStep.Principal = assigner; - newStep.UserPrompt = userPrompt; + newStep.UserPrompt = userPrompt; builder.WorkflowBuilder.AddStep(newStep); var stepBuilder = new StepBuilder(builder.WorkflowBuilder, newStep); @@ -64,5 +64,23 @@ public static IUserTaskBuilder UserTask(this IStepBuild return stepBuilder; } + + public static IUserTaskBuilder UserTask(this IStepBuilder builder, string userPrompt, Expression> assigner, Action> stepSetup = null) + where TStepBody : IStepBody + { + var newStep = new UserTaskStep(); + builder.WorkflowBuilder.AddStep(newStep); + var stepBuilder = new UserTaskBuilder(builder.WorkflowBuilder, newStep); + stepBuilder.Input(step => step.AssignedPrincipal, assigner); + stepBuilder.Input(step => step.Prompt, data => userPrompt); + + if (stepSetup != null) + stepSetup.Invoke(stepBuilder); + + newStep.Name = newStep.Name ?? typeof(UserTask).Name; + builder.Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); + + return stepBuilder; + } } } From 717eb16f45347eddf121c2b69dfc90f95ef63f86 Mon Sep 17 00:00:00 2001 From: Ivan Josipovic <9521987+IvanJosipovic@users.noreply.github.com> Date: Fri, 30 Sep 2022 18:07:40 -0700 Subject: [PATCH 424/462] feat: add IStepExecutionContext to If When ForEach --- .../Interface/IWorkflowModifier.cs | 23 +++++- .../Services/FluentBuilders/StepBuilder.cs | 73 +++++++++++++++---- .../FluentBuilders/WorkflowBuilder.cs | 15 ++++ 3 files changed, 97 insertions(+), 14 deletions(-) diff --git a/src/WorkflowCore/Interface/IWorkflowModifier.cs b/src/WorkflowCore/Interface/IWorkflowModifier.cs index 1ecd093d0..835855e97 100644 --- a/src/WorkflowCore/Interface/IWorkflowModifier.cs +++ b/src/WorkflowCore/Interface/IWorkflowModifier.cs @@ -83,7 +83,7 @@ IStepBuilder WaitFor(string eventName, /// Resolves a collection for iterate over /// IContainerStepBuilder ForEach(Expression> collection); - + /// /// Execute a block of steps, once for each item in a collection in a RunParallel foreach /// @@ -91,6 +91,13 @@ IStepBuilder WaitFor(string eventName, /// IContainerStepBuilder ForEach(Expression> collection, Expression> runParallel); + /// + /// Execute a block of steps, once for each item in a collection in a RunParallel foreach + /// + /// Resolves a collection for iterate over + /// + IContainerStepBuilder ForEach(Expression> collection, Expression> runParallel); + /// /// Repeat a block of steps until a condition becomes true /// @@ -98,6 +105,13 @@ IStepBuilder WaitFor(string eventName, /// IContainerStepBuilder While(Expression> condition); + /// + /// Repeat a block of steps until a condition becomes true + /// + /// Resolves a condition to break out of the while loop + /// + IContainerStepBuilder While(Expression> condition); + /// /// Execute a block of steps if a condition is true /// @@ -105,6 +119,13 @@ IStepBuilder WaitFor(string eventName, /// IContainerStepBuilder If(Expression> condition); + /// + /// Execute a block of steps if a condition is true + /// + /// Resolves a condition to evaluate + /// + IContainerStepBuilder If(Expression> condition); + /// /// Configure an outcome for this step, then wire it to a sequence /// diff --git a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs index 9cfeae852..9e41f15ca 100644 --- a/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs @@ -59,7 +59,7 @@ public IStepBuilder Then(IStepBuilder newStep } public IStepBuilder Then(Func body) - { + { WorkflowStepInline newStep = new WorkflowStepInline(); newStep.Body = body; WorkflowBuilder.AddStep(newStep); @@ -100,7 +100,7 @@ public IStepOutcomeBuilder When(object outcomeValue, string label = null) var outcomeBuilder = new StepOutcomeBuilder(WorkflowBuilder, result); return outcomeBuilder; } - + public IStepBuilder Branch(object outcomeValue, IStepBuilder branch) where TStep : IStepBody { if (branch.WorkflowBuilder.Steps.Count == 0) @@ -123,10 +123,10 @@ public IStepBuilder Branch(Expression(outcomeExpression) - { + { NextStep = branch.WorkflowBuilder.Steps[0].Id }); @@ -155,7 +155,7 @@ public IStepBuilder Input(Action(action)); return this; - } + } public IStepBuilder Output(Expression> dataProperty, Expression> value) { @@ -206,7 +206,7 @@ public IStepBuilder WaitFor(string eventName, Expression End(string name) where TStep : IStepBody { var ancestor = IterateParents(Step.Id, name); @@ -294,12 +294,12 @@ public IStepBuilder Decide(Expression> expres public IContainerStepBuilder ForEach(Expression> collection) { var newStep = new WorkflowStep(); - + Expression> inputExpr = (x => x.Collection); - newStep.Inputs.Add(new MemberMapParameter(collection, inputExpr)); + newStep.Inputs.Add(new MemberMapParameter(collection, inputExpr)); WorkflowBuilder.AddStep(newStep); - var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); + var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); @@ -324,6 +324,23 @@ public IContainerStepBuilder ForEach(Expression ForEach(Expression> collection, Expression> runParallel) + { + var newStep = new WorkflowStep(); + + Expression> inputExpr = (x => x.Collection); + newStep.Inputs.Add(new MemberMapParameter(collection, inputExpr)); + + Expression> pExpr = (x => x.RunParallel); + newStep.Inputs.Add(new MemberMapParameter(runParallel, pExpr)); + + WorkflowBuilder.AddStep(newStep); + var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); + + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); + + return stepBuilder; + } public IContainerStepBuilder While(Expression> condition) { @@ -340,6 +357,21 @@ public IContainerStepBuilder While(Expression While(Expression> condition) + { + var newStep = new WorkflowStep(); + + Expression> inputExpr = (x => x.Condition); + newStep.Inputs.Add(new MemberMapParameter(condition, inputExpr)); + + WorkflowBuilder.AddStep(newStep); + var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); + + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); + + return stepBuilder; + } + public IContainerStepBuilder If(Expression> condition) { var newStep = new WorkflowStep(); @@ -354,7 +386,22 @@ public IContainerStepBuilder If(Expression> con return stepBuilder; } - + + public IContainerStepBuilder If(Expression> condition) + { + var newStep = new WorkflowStep(); + + Expression> inputExpr = (x => x.Condition); + newStep.Inputs.Add(new MemberMapParameter(condition, inputExpr)); + + WorkflowBuilder.AddStep(newStep); + var stepBuilder = new StepBuilder(WorkflowBuilder, newStep); + + Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id }); + + return stepBuilder; + } + public IContainerStepBuilder When(Expression> outcomeValue, string label = null) { var newStep = new WorkflowStep(); @@ -378,7 +425,7 @@ public IContainerStepBuilder When(Expression); } - + WorkflowBuilder.AddStep(newStep); var stepBuilder = new ReturnStepBuilder(WorkflowBuilder, newStep, switchBuilder); switchBuilder.Step.Children.Add(newStep.Id); @@ -512,10 +559,10 @@ public IStepBuilder Activity(string activityName, Expression(WorkflowBuilder, newStep); stepBuilder.Input((step) => step.ActivityName, (data) => activityName); - + if (parameters != null) stepBuilder.Input((step) => step.Parameters, parameters); - + if (effectiveDate != null) stepBuilder.Input((step) => step.EffectiveDate, effectiveDate); diff --git a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs index 64950f46d..adc02f488 100644 --- a/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs +++ b/src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs @@ -241,16 +241,31 @@ public IContainerStepBuilder ForEach(Expression ForEach(Expression> collection, Expression> runParallel) + { + return Start().ForEach(collection, runParallel); + } + public IContainerStepBuilder While(Expression> condition) { return Start().While(condition); } + public IContainerStepBuilder While(Expression> condition) + { + return Start().While(condition); + } + public IContainerStepBuilder If(Expression> condition) { return Start().If(condition); } + public IContainerStepBuilder If(Expression> condition) + { + return Start().If(condition); + } + public IContainerStepBuilder When(Expression> outcomeValue, string label = null) { return ((IWorkflowModifier) Start()).When(outcomeValue, label); From 97974b2199dfbd40752bb35f4f2f191a4e0dd4b5 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 3 Nov 2022 08:44:56 -0700 Subject: [PATCH 425/462] Create FUNDING.yml --- .github/FUNDING.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..21a4c65b6 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [danielgerlag] From 0908116cdc9c87a76c11c6d338f90ad29c107fef Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 13:44:34 +0100 Subject: [PATCH 426/462] Fix mongodb index for GetRunnableInstances --- .../Services/MongoPersistenceProvider.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 2824f9674..0d238ec64 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -93,8 +93,10 @@ static void CreateIndexes(MongoPersistenceProvider instance) if (!indexesCreated) { instance.WorkflowInstances.Indexes.CreateOne(new CreateIndexModel( - Builders.IndexKeys.Ascending(x => x.NextExecution), - new CreateIndexOptions {Background = true, Name = "idx_nextExec"})); + Builders.IndexKeys + .Ascending(x => x.NextExecution) + .Ascending(x => x.Status), + new CreateIndexOptions {Background = true, Name = "idx_nextExec_v2"})); instance.Events.Indexes.CreateOne(new CreateIndexModel( Builders.IndexKeys.Ascending(x => x.IsProcessed), From b227d6f3a60bcbffc3c42f6e3b71213aed554248 Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 13:51:22 +0100 Subject: [PATCH 427/462] Disable table scan for tests --- test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs index 9ce3ce7d7..3a2c0d624 100644 --- a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs +++ b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs @@ -1,5 +1,6 @@ using System; using Docker.Testify; +using MongoDB.Bson; using MongoDB.Driver; using Xunit; @@ -23,6 +24,10 @@ public override bool TestReady() { var client = new MongoClient($"mongodb://localhost:{ExternalPort}"); client.ListDatabases(); + + var command = new BsonDocument { { "setParameter", 1 }, { "notablescan", 1 } }; + client.GetDatabase("admin").RunCommand(command); + return true; } catch From b878221866ce64bc77f26de3d6e41a09020d571a Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 15:01:48 +0100 Subject: [PATCH 428/462] Switch postgress and mongo tests to Squadron --- .../MongoDockerSetup.cs | 41 +++++++----------- .../WorkflowCore.Tests.MongoDB.csproj | 5 ++- .../DockerSetup.cs | 43 +++++++------------ .../WorkflowCore.Tests.PostgreSQL.csproj | 5 ++- 4 files changed, 37 insertions(+), 57 deletions(-) diff --git a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs index 3a2c0d624..b7214ea73 100644 --- a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs +++ b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs @@ -1,40 +1,32 @@ using System; -using Docker.Testify; +using System.Threading.Tasks; using MongoDB.Bson; -using MongoDB.Driver; +using Squadron; using Xunit; namespace WorkflowCore.Tests.MongoDB { - public class MongoDockerSetup : DockerSetup + public class MongoDockerSetup : IAsyncLifetime { + private readonly MongoResource _mongoResource; public static string ConnectionString { get; set; } - public override string ImageName => "mongo"; - public override int InternalPort => 27017; - - public override void PublishConnectionInfo() + public MongoDockerSetup() { - ConnectionString = $"mongodb://localhost:{ExternalPort}"; + _mongoResource = new MongoResource(); } - - public override bool TestReady() + + public async Task InitializeAsync() { - try - { - var client = new MongoClient($"mongodb://localhost:{ExternalPort}"); - client.ListDatabases(); - - var command = new BsonDocument { { "setParameter", 1 }, { "notablescan", 1 } }; - client.GetDatabase("admin").RunCommand(command); - - return true; - } - catch - { - return false; - } + await _mongoResource.InitializeAsync(); + var command = new BsonDocument { { "setParameter", 1 }, { "notablescan", 1 } }; + _mongoResource.Client.GetDatabase("admin").RunCommand(command); + ConnectionString = _mongoResource.ConnectionString; + } + public Task DisposeAsync() + { + return _mongoResource.DisposeAsync(); } } @@ -42,5 +34,4 @@ public override bool TestReady() public class MongoCollection : ICollectionFixture { } - } diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index 98fdd4ce8..048f3d994 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -24,8 +24,9 @@ - - + + + diff --git a/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs b/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs index 253493803..3f7739ee9 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs @@ -1,43 +1,31 @@ using System; -using System.Collections.Generic; -using Docker.Testify; -using Npgsql; +using System.Threading.Tasks; +using Squadron; using Xunit; namespace WorkflowCore.Tests.PostgreSQL { - public class PostgresDockerSetup : DockerSetup + public class PostgresDockerSetup : IAsyncLifetime { + private readonly PostgreSqlResource _postgreSqlResource; public static string ConnectionString { get; set; } public static string ScenarioConnectionString { get; set; } - public override string ImageName => "postgres"; - public override int InternalPort => 5432; - - private const string PostgresHostAuthMethod = "trust"; - public override IList EnvironmentVariables => new List { - $"POSTGRES_HOST_AUTH_METHOD={PostgresHostAuthMethod}" - }; - public override void PublishConnectionInfo() + public PostgresDockerSetup() { - ConnectionString = $"Server=127.0.0.1;Port={ExternalPort};Database=workflow;User Id=postgres;"; - ScenarioConnectionString = $"Server=127.0.0.1;Port={ExternalPort};Database=workflow-scenarios;User Id=postgres;"; + _postgreSqlResource = new PostgreSqlResource(); } - - public override bool TestReady() + + public async Task InitializeAsync() { - try - { - var connection = new NpgsqlConnection($"Server=127.0.0.1;Port={ExternalPort};Database=postgres;User Id=postgres;"); - connection.Open(); - connection.Close(); - return true; - } - catch - { - return false; - } + await _postgreSqlResource.InitializeAsync(); + ConnectionString = _postgreSqlResource.ConnectionString; + ScenarioConnectionString = _postgreSqlResource.ConnectionString; + } + public Task DisposeAsync() + { + return _postgreSqlResource.DisposeAsync(); } } @@ -45,5 +33,4 @@ public override bool TestReady() public class PostgresCollection : ICollectionFixture { } - } diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index 38cdefaba..a228ea763 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -21,8 +21,9 @@ - - + + + From 913b486b3d4dbb90b02c8b499b2799c8e911ab26 Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 15:03:30 +0100 Subject: [PATCH 429/462] Add mongodb tests to pipeline --- .github/workflows/dotnet.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 9a49e0237..181960023 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -27,6 +27,8 @@ jobs: run: dotnet test test/WorkflowCore.UnitTests --no-build --verbosity normal -p:ParallelizeTestCollections=false - name: Integration Tests run: dotnet test test/WorkflowCore.IntegrationTests --no-build --verbosity normal -p:ParallelizeTestCollections=false + - name: MongoDB Tests + run: dotnet test test/WorkflowCore.Tests.MongoDB --no-build --verbosity normal -p:ParallelizeTestCollections=false - name: PostgreSQL Tests run: dotnet test test/WorkflowCore.Tests.PostgreSQL --no-build --verbosity normal -p:ParallelizeTestCollections=false - name: Redis Tests From c5aac199573a410e3abe27095fa01872bf970d4d Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 15:08:11 +0100 Subject: [PATCH 430/462] Remove table scan deactivation from tests --- test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs index b7214ea73..139fef30f 100644 --- a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs +++ b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs @@ -19,8 +19,9 @@ public MongoDockerSetup() public async Task InitializeAsync() { await _mongoResource.InitializeAsync(); - var command = new BsonDocument { { "setParameter", 1 }, { "notablescan", 1 } }; - _mongoResource.Client.GetDatabase("admin").RunCommand(command); + // TODO: Should be enabled + // var command = new BsonDocument { { "setParameter", 1 }, { "notablescan", 1 } }; + // _mongoResource.Client.GetDatabase("admin").RunCommand(command); ConnectionString = _mongoResource.ConnectionString; } From 1571e78195c05c000434bd6628d12d65821095e5 Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 15:16:37 +0100 Subject: [PATCH 431/462] Run tests in parallel --- .github/workflows/dotnet.yml | 112 ++++++++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 15 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 181960023..8bb2947f6 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -7,10 +7,8 @@ on: branches: [ master ] jobs: - build: - + Unit-Tests: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Setup .NET @@ -25,15 +23,99 @@ jobs: run: dotnet build --no-restore - name: Unit Tests run: dotnet test test/WorkflowCore.UnitTests --no-build --verbosity normal -p:ParallelizeTestCollections=false - - name: Integration Tests - run: dotnet test test/WorkflowCore.IntegrationTests --no-build --verbosity normal -p:ParallelizeTestCollections=false - - name: MongoDB Tests - run: dotnet test test/WorkflowCore.Tests.MongoDB --no-build --verbosity normal -p:ParallelizeTestCollections=false - - name: PostgreSQL Tests - run: dotnet test test/WorkflowCore.Tests.PostgreSQL --no-build --verbosity normal -p:ParallelizeTestCollections=false - - name: Redis Tests - run: dotnet test test/WorkflowCore.Tests.Redis --no-build --verbosity normal -p:ParallelizeTestCollections=false - - name: SQL Server Tests - run: dotnet test test/WorkflowCore.Tests.SqlServer --no-build --verbosity normal -p:ParallelizeTestCollections=false - - name: Elasticsearch Tests - run: dotnet test test/WorkflowCore.Tests.Elasticsearch --no-build --verbosity normal -p:ParallelizeTestCollections=false + Integration-Tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: | + 3.1.x + 6.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Integration Tests + run: dotnet test test/WorkflowCore.IntegrationTests --no-build --verbosity normal -p:ParallelizeTestCollections=false + MongoDB-Tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: | + 3.1.x + 6.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: MongoDB Tests + run: dotnet test test/WorkflowCore.Tests.MongoDB --no-build --verbosity normal -p:ParallelizeTestCollections=false + PostgreSQL-Tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: | + 3.1.x + 6.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: PostgreSQL Tests + run: dotnet test test/WorkflowCore.Tests.PostgreSQL --no-build --verbosity normal -p:ParallelizeTestCollections=false + Redis-Tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: | + 3.1.x + 6.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Redis Tests + run: dotnet test test/WorkflowCore.Tests.Redis --no-build --verbosity normal -p:ParallelizeTestCollections=false + SQLServer-Tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: | + 3.1.x + 6.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: SQL Server Tests + run: dotnet test test/WorkflowCore.Tests.SqlServer --no-build --verbosity normal -p:ParallelizeTestCollections=false + Elasticsearch-Tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: | + 3.1.x + 6.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Elasticsearch Tests + run: dotnet test test/WorkflowCore.Tests.Elasticsearch --no-build --verbosity normal -p:ParallelizeTestCollections=false \ No newline at end of file From a726680aea97d907185b767abde67fa126092884 Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 15:21:07 +0100 Subject: [PATCH 432/462] Switch redis to Squadron --- .../RedisDockerSetup.cs | 32 ++++++++----------- .../WorkflowCore.Tests.Redis.csproj | 5 +-- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs b/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs index 91fd3e32a..0cc13c8b2 100644 --- a/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs +++ b/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs @@ -1,34 +1,29 @@ using System; -using Docker.Testify; -using StackExchange.Redis; +using System.Threading.Tasks; +using Squadron; using Xunit; namespace WorkflowCore.Tests.Redis { - public class RedisDockerSetup : DockerSetup + public class RedisDockerSetup : IAsyncLifetime { + private readonly RedisResource _redisResource; public static string ConnectionString { get; set; } - public override string ImageName => @"redis"; - public override int InternalPort => 6379; - - public override void PublishConnectionInfo() + public RedisDockerSetup() { - ConnectionString = $"localhost:{ExternalPort}"; + _redisResource = new RedisResource(); } - public override bool TestReady() + public async Task InitializeAsync() { - try - { - var multiplexer = ConnectionMultiplexer.Connect($"localhost:{ExternalPort}"); - return multiplexer.IsConnected; - } - catch - { - return false; - } + await _redisResource.InitializeAsync(); + ConnectionString = _redisResource.ConnectionString; + } + public Task DisposeAsync() + { + return _redisResource.DisposeAsync(); } } @@ -36,5 +31,4 @@ public override bool TestReady() public class RedisCollection : ICollectionFixture { } - } diff --git a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj index 0ee3705bf..ddb796d70 100644 --- a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj +++ b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj @@ -3,8 +3,9 @@ - - + + + From a6a2d153745927b8861344b4e2a1bfe49c6d01a2 Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 15:23:44 +0100 Subject: [PATCH 433/462] Switch SQLServer tests --- .../DockerSetup.cs | 42 +++++++------------ .../WorkflowCore.Tests.SqlServer.csproj | 5 ++- 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs b/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs index b9d15ddff..147c09241 100644 --- a/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs +++ b/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs @@ -1,44 +1,31 @@ using System; -using System.Collections.Generic; -using System.Data.SqlClient; -using Docker.Testify; +using System.Threading.Tasks; +using Squadron; using Xunit; namespace WorkflowCore.Tests.SqlServer { - public class SqlDockerSetup : DockerSetup + public class SqlDockerSetup : IAsyncLifetime { + private readonly SqlServerResource _sqlServerResource; public static string ConnectionString { get; set; } public static string ScenarioConnectionString { get; set; } - public override string ImageName => "mcr.microsoft.com/mssql/server"; - public override int InternalPort => 1433; - public override TimeSpan TimeOut => TimeSpan.FromSeconds(120); - - public const string SqlPassword = "I@mJustT3st1ing"; - - public override IList EnvironmentVariables => new List {"ACCEPT_EULA=Y", $"SA_PASSWORD={SqlPassword}"}; - - public override void PublishConnectionInfo() + public SqlDockerSetup() { - ConnectionString = $"Server=127.0.0.1,{ExternalPort};Database=workflowcore-tests;User Id=sa;Password={SqlPassword};"; - ScenarioConnectionString = $"Server=127.0.0.1,{ExternalPort};Database=workflowcore-scenario-tests;User Id=sa;Password={SqlPassword};"; + _sqlServerResource = new SqlServerResource(); } - public override bool TestReady() + public async Task InitializeAsync() { - try - { - var client = new SqlConnection($"Server=127.0.0.1,{ExternalPort};Database=master;User Id=sa;Password={SqlPassword};"); - client.Open(); - client.Close(); - return true; - } - catch - { - return false; - } + await _sqlServerResource.InitializeAsync(); + ConnectionString = _sqlServerResource.CreateConnectionString("workflowcore-tests"); + ScenarioConnectionString = _sqlServerResource.CreateConnectionString("workflowcore-scenario-tests"); + } + public Task DisposeAsync() + { + return _sqlServerResource.DisposeAsync(); } } @@ -46,5 +33,4 @@ public override bool TestReady() public class SqlServerCollection : ICollectionFixture { } - } \ No newline at end of file diff --git a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj index 36ee2a15d..aa93036ed 100644 --- a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj +++ b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj @@ -2,8 +2,9 @@ - - + + + From dac4917c7ddb5871d1653f3edbf85638dd6a5cf6 Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 15:30:27 +0100 Subject: [PATCH 434/462] Switch elasticsearch tests --- .../ElasticsearchDockerSetup.cs | 39 +++++++------------ .../WorkflowCore.Tests.Elasticsearch.csproj | 5 ++- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs index 4bb1c57f9..6690c2732 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs +++ b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs @@ -1,42 +1,29 @@ using System; -using System.Collections.Generic; -using Docker.Testify; -using Nest; +using System.Threading.Tasks; +using Squadron; using Xunit; namespace WorkflowCore.Tests.Elasticsearch { - public class ElasticsearchDockerSetup : DockerSetup + public class ElasticsearchDockerSetup : IAsyncLifetime { + private readonly ElasticsearchResource _elasticsearchResource; public static string ConnectionString { get; set; } - - public override string ImageName => @"elasticsearch"; - public override string ImageTag => "7.5.1"; - public override int InternalPort => 9200; - public override TimeSpan TimeOut => TimeSpan.FromSeconds(30); - - public override IList EnvironmentVariables => new List { - $"discovery.type=single-node" - }; - public override void PublishConnectionInfo() + public ElasticsearchDockerSetup() { - ConnectionString = $"http://localhost:{ExternalPort}"; + _elasticsearchResource = new ElasticsearchResource(); } - public override bool TestReady() + public async Task InitializeAsync() { - try - { - var client = new ElasticClient(new ConnectionSettings(new Uri($"http://localhost:{ExternalPort}"))); - var ping = client.Ping(); - return ping.IsValid; - } - catch - { - return false; - } + await _elasticsearchResource.InitializeAsync(); + ConnectionString = $"http://localhost:{_elasticsearchResource.Instance.HostPort}"; + } + public Task DisposeAsync() + { + return _elasticsearchResource.DisposeAsync(); } } diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 887e2df63..3050549bd 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -3,8 +3,9 @@ - - + + + From e77013106ca5b41ac6c6f841dc4b835efc3a5daa Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 15:41:06 +0100 Subject: [PATCH 435/462] Consolidate tests projects dependencies --- test/Directory.Build.props | 21 ++++++++ .../WorkflowCore.IntegrationTests.csproj | 8 ---- .../WorkflowCore.TestAssets.csproj | 2 - .../WorkflowCore.Tests.DynamoDB.csproj | 3 -- .../WorkflowCore.Tests.Elasticsearch.csproj | 3 -- .../WorkflowCore.Tests.MongoDB.csproj | 6 --- test/WorkflowCore.Tests.MySQL/DockerSetup.cs | 48 +++++++------------ .../WorkflowCore.Tests.MySQL.csproj | 4 +- .../WorkflowCore.Tests.PostgreSQL.csproj | 3 -- ...wCore.Tests.QueueProviders.RabbitMQ.csproj | 4 -- .../WorkflowCore.Tests.Redis.csproj | 3 -- .../WorkflowCore.Tests.SqlServer.csproj | 3 -- .../WorkflowCore.Tests.Sqlite.csproj | 6 --- .../WorkflowCore.UnitTests.csproj | 11 ----- 14 files changed, 38 insertions(+), 87 deletions(-) diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 0f8bd594d..4d9ba3103 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -4,4 +4,25 @@ latest false + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj index 39390c2e6..bdc973728 100644 --- a/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj +++ b/test/WorkflowCore.IntegrationTests/WorkflowCore.IntegrationTests.csproj @@ -18,14 +18,6 @@ - - - - - - - - diff --git a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj index 4530229d1..cbba0dccb 100644 --- a/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj +++ b/test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj @@ -38,8 +38,6 @@ - - diff --git a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj index 85593c9bb..65320489d 100644 --- a/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj +++ b/test/WorkflowCore.Tests.DynamoDB/WorkflowCore.Tests.DynamoDB.csproj @@ -2,9 +2,6 @@ - - - diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 3050549bd..8702a25da 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -1,10 +1,7 @@  - - - diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index 048f3d994..68251d271 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -20,13 +20,7 @@ - - - - - - diff --git a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs index 05758f4c1..8a33f50fd 100644 --- a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs @@ -1,46 +1,31 @@ -using System.Collections.Generic; -using Docker.Testify; -using Xunit; +using Xunit; using System; -using MySqlConnector; +using System.Threading.Tasks; +using Squadron; namespace WorkflowCore.Tests.MySQL { - public class MysqlDockerSetup : DockerSetup + public class MysqlDockerSetup : IAsyncLifetime { + private readonly MySqlResource _mySqlResource; public static string ConnectionString { get; set; } public static string ScenarioConnectionString { get; set; } - public static string RootPassword => "rootpwd123"; - public override TimeSpan TimeOut => TimeSpan.FromSeconds(60); - - public override string ImageName => "mysql"; - public override IList EnvironmentVariables => new List { - $"MYSQL_ROOT_PASSWORD={RootPassword}" - }; - - public override int InternalPort => 3306; - - public override void PublishConnectionInfo() + public MysqlDockerSetup() { - ConnectionString = $"Server=127.0.0.1;Port={ExternalPort};Database=workflow;User=root;Password={RootPassword};"; - ScenarioConnectionString = $"Server=127.0.0.1;Port={ExternalPort};Database=scenarios;User=root;Password={RootPassword};"; + _mySqlResource = new MySqlResource(); } - - public override bool TestReady() + + public async Task InitializeAsync() { - try - { - var connection = new MySqlConnection($"host=127.0.0.1;port={ExternalPort};user=root;password={RootPassword};database=mysql;"); - connection.Open(); - connection.Close(); - return true; - } - catch - { - return false; - } + await _mySqlResource.InitializeAsync(); + ConnectionString = _mySqlResource.ConnectionString; + ScenarioConnectionString = _mySqlResource.ConnectionString; + } + public Task DisposeAsync() + { + return _mySqlResource.DisposeAsync(); } } @@ -48,5 +33,4 @@ public override bool TestReady() public class MysqlCollection : ICollectionFixture { } - } diff --git a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj index 1432348e2..cfdb4163d 100644 --- a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj +++ b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj @@ -1,9 +1,7 @@ - - - + diff --git a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj index a228ea763..66cc649b4 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj +++ b/test/WorkflowCore.Tests.PostgreSQL/WorkflowCore.Tests.PostgreSQL.csproj @@ -20,9 +20,6 @@ - - - diff --git a/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj b/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj index 0173d76bd..98e658fed 100644 --- a/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj +++ b/test/WorkflowCore.Tests.QueueProviders.RabbitMQ/WorkflowCore.Tests.QueueProviders.RabbitMQ.csproj @@ -1,10 +1,6 @@ - - - - diff --git a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj index ddb796d70..147dad6da 100644 --- a/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj +++ b/test/WorkflowCore.Tests.Redis/WorkflowCore.Tests.Redis.csproj @@ -1,10 +1,7 @@ - - - diff --git a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj index aa93036ed..24e83536e 100644 --- a/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj +++ b/test/WorkflowCore.Tests.SqlServer/WorkflowCore.Tests.SqlServer.csproj @@ -1,9 +1,6 @@  - - - diff --git a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj index 31f839cf3..63acee283 100644 --- a/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj +++ b/test/WorkflowCore.Tests.Sqlite/WorkflowCore.Tests.Sqlite.csproj @@ -1,11 +1,5 @@  - - - - - - diff --git a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj index f6ae01fbf..3ca6d6038 100644 --- a/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj +++ b/test/WorkflowCore.UnitTests/WorkflowCore.UnitTests.csproj @@ -15,17 +15,6 @@ - - - - - - - - - - - From ef9b176c10af472e83530834d7653f5d84e7fcd0 Mon Sep 17 00:00:00 2001 From: glucaci Date: Tue, 22 Nov 2022 15:54:32 +0100 Subject: [PATCH 436/462] Fix dependencies --- test/Directory.Build.props | 8 ++------ test/ScratchPad/ScratchPad.csproj | 1 + .../WorkflowCore.Tests.Elasticsearch.csproj | 1 - 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 4d9ba3103..3b2ad7341 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -9,20 +9,16 @@ - + - - - + - - \ No newline at end of file diff --git a/test/ScratchPad/ScratchPad.csproj b/test/ScratchPad/ScratchPad.csproj index fdd4f1a3d..a194035c2 100644 --- a/test/ScratchPad/ScratchPad.csproj +++ b/test/ScratchPad/ScratchPad.csproj @@ -7,6 +7,7 @@ false false false + false diff --git a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj index 8702a25da..9aa005f40 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj +++ b/test/WorkflowCore.Tests.Elasticsearch/WorkflowCore.Tests.Elasticsearch.csproj @@ -1,7 +1,6 @@  - From c37d4bbd9f9b7efbae6795f09151b88917f5fe76 Mon Sep 17 00:00:00 2001 From: glucaci Date: Thu, 24 Nov 2022 08:47:42 +0100 Subject: [PATCH 437/462] Isolate MongoDB tests --- .../MongoPersistenceProviderFixture.cs | 2 +- .../Scenarios/MongoActivityScenario.cs | 2 +- test/WorkflowCore.Tests.MongoDB/Scenarios/MongoBasicScenario.cs | 2 +- .../Scenarios/MongoCompensationScenario.cs | 2 +- test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs | 2 +- .../Scenarios/MongoDecisionScenario.cs | 2 +- test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDelayScenario.cs | 2 +- test/WorkflowCore.Tests.MongoDB/Scenarios/MongoEventScenario.cs | 2 +- .../Scenarios/MongoForEachScenario.cs | 2 +- test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForkScenario.cs | 2 +- test/WorkflowCore.Tests.MongoDB/Scenarios/MongoIfScenario.cs | 2 +- .../Scenarios/MongoRetrySagaScenario.cs | 2 +- test/WorkflowCore.Tests.MongoDB/Scenarios/MongoSagaScenario.cs | 2 +- test/WorkflowCore.Tests.MongoDB/Scenarios/MongoUserScenario.cs | 2 +- test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhenScenario.cs | 2 +- test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhileScenario.cs | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/WorkflowCore.Tests.MongoDB/MongoPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.MongoDB/MongoPersistenceProviderFixture.cs index 79de07a97..c61b4b5e2 100644 --- a/test/WorkflowCore.Tests.MongoDB/MongoPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.MongoDB/MongoPersistenceProviderFixture.cs @@ -22,7 +22,7 @@ protected override IPersistenceProvider Subject get { var client = new MongoClient(MongoDockerSetup.ConnectionString); - var db = client.GetDatabase("workflow-tests"); + var db = client.GetDatabase(nameof(MongoPersistenceProviderFixture)); return new MongoPersistenceProvider(db); } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs index 52625678f..ca815f41a 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoActivityScenario.cs @@ -14,7 +14,7 @@ protected override void ConfigureServices(IServiceCollection services) BsonClassMap.RegisterClassMap(x => x.AutoMap()); BsonClassMap.RegisterClassMap(x => x.AutoMap()); - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoActivityScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoBasicScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoBasicScenario.cs index bec7c6d50..c24ffe46c 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoBasicScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoBasicScenario.cs @@ -10,7 +10,7 @@ public class MongoBasicScenario : BasicScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoBasicScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoCompensationScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoCompensationScenario.cs index 8adf4b4bd..ed3114e55 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoCompensationScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoCompensationScenario.cs @@ -10,7 +10,7 @@ public class MongoCompensationScenario : CompensationScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoCompensationScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs index e842f6fee..6c647bebd 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDataScenario.cs @@ -16,7 +16,7 @@ public MongoDataScenario() : base() protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoDataScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs index b5f8fd01f..0db093806 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDecisionScenario.cs @@ -10,7 +10,7 @@ public class MongoDecisionScenario : DecisionScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoDecisionScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDelayScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDelayScenario.cs index 511aa79fb..45aca549f 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDelayScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDelayScenario.cs @@ -18,7 +18,7 @@ protected override void ConfigureServices(IServiceCollection services) { services.AddWorkflow(cfg => { - cfg.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests"); + cfg.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoDelayScenario)); cfg.UsePollInterval(TimeSpan.FromSeconds(2)); }); } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoEventScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoEventScenario.cs index b789bab5b..2aa6e8461 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoEventScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoEventScenario.cs @@ -10,7 +10,7 @@ public class MongoEventScenario : EventScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoEventScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForEachScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForEachScenario.cs index c8b718753..5fa8b94b8 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForEachScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForEachScenario.cs @@ -10,7 +10,7 @@ public class MongoForEachScenario : ForeachScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoForEachScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForkScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForkScenario.cs index 319af0466..f582f0e7b 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForkScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoForkScenario.cs @@ -10,7 +10,7 @@ public class MongoForkScenario : ForkScenario { protected override void Configure(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoForkScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoIfScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoIfScenario.cs index 7ea42fd98..5c78d3ddc 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoIfScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoIfScenario.cs @@ -10,7 +10,7 @@ public class MongoIfScenario : IfScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoIfScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoRetrySagaScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoRetrySagaScenario.cs index b8a9ec92b..5e30ed877 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoRetrySagaScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoRetrySagaScenario.cs @@ -10,7 +10,7 @@ public class MongoRetrySagaScenario : RetrySagaScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoRetrySagaScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoSagaScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoSagaScenario.cs index e5d666a9c..c9ceb9d83 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoSagaScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoSagaScenario.cs @@ -10,7 +10,7 @@ public class MongoSagaScenario : SagaScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoSagaScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoUserScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoUserScenario.cs index 93ec848ee..fbaa0f223 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoUserScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoUserScenario.cs @@ -10,7 +10,7 @@ public class MongoUserScenario : UserScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoUserScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhenScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhenScenario.cs index 6cca92b6b..b966f35b8 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhenScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhenScenario.cs @@ -10,7 +10,7 @@ public class MongoWhenScenario : WhenScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoWhenScenario))); } } } diff --git a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhileScenario.cs b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhileScenario.cs index 4c7aaead9..78e5277f6 100644 --- a/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhileScenario.cs +++ b/test/WorkflowCore.Tests.MongoDB/Scenarios/MongoWhileScenario.cs @@ -10,7 +10,7 @@ public class MongoWhileScenario : WhileScenario { protected override void ConfigureServices(IServiceCollection services) { - services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests")); + services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, nameof(MongoWhileScenario))); } } } From c955bc8dff328d9eb7079f2626ba9fbb63fb7e65 Mon Sep 17 00:00:00 2001 From: glucaci Date: Thu, 24 Nov 2022 08:59:37 +0100 Subject: [PATCH 438/462] Fix postgres tests --- test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs b/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs index 3f7739ee9..1e628713f 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs @@ -13,6 +13,7 @@ public class PostgresDockerSetup : IAsyncLifetime public PostgresDockerSetup() { + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); _postgreSqlResource = new PostgreSqlResource(); } From fde25bf1019851ad61dedfb6cd37279750875f8a Mon Sep 17 00:00:00 2001 From: glucaci Date: Thu, 24 Nov 2022 09:32:26 +0100 Subject: [PATCH 439/462] Cleanup --- .../ElasticsearchDockerSetup.cs | 2 +- test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs | 8 ++++---- test/WorkflowCore.Tests.MySQL/DockerSetup.cs | 4 ++-- test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs | 8 ++++---- test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs | 6 +++--- test/WorkflowCore.Tests.SqlServer/DockerSetup.cs | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs index 6690c2732..4ed16a152 100644 --- a/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs +++ b/test/WorkflowCore.Tests.Elasticsearch/ElasticsearchDockerSetup.cs @@ -31,4 +31,4 @@ public Task DisposeAsync() public class ElasticsearchCollection : ICollectionFixture { } -} +} \ No newline at end of file diff --git a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs index 139fef30f..7b956196d 100644 --- a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs +++ b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs @@ -5,7 +5,7 @@ using Xunit; namespace WorkflowCore.Tests.MongoDB -{ +{ public class MongoDockerSetup : IAsyncLifetime { private readonly MongoResource _mongoResource; @@ -15,7 +15,7 @@ public MongoDockerSetup() { _mongoResource = new MongoResource(); } - + public async Task InitializeAsync() { await _mongoResource.InitializeAsync(); @@ -33,6 +33,6 @@ public Task DisposeAsync() [CollectionDefinition("Mongo collection")] public class MongoCollection : ICollectionFixture - { + { } -} +} \ No newline at end of file diff --git a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs index 8a33f50fd..9444a8005 100644 --- a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs @@ -15,7 +15,7 @@ public MysqlDockerSetup() { _mySqlResource = new MySqlResource(); } - + public async Task InitializeAsync() { await _mySqlResource.InitializeAsync(); @@ -33,4 +33,4 @@ public Task DisposeAsync() public class MysqlCollection : ICollectionFixture { } -} +} \ No newline at end of file diff --git a/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs b/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs index 1e628713f..8548bf85d 100644 --- a/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.PostgreSQL/DockerSetup.cs @@ -16,7 +16,7 @@ public PostgresDockerSetup() AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); _postgreSqlResource = new PostgreSqlResource(); } - + public async Task InitializeAsync() { await _postgreSqlResource.InitializeAsync(); @@ -29,9 +29,9 @@ public Task DisposeAsync() return _postgreSqlResource.DisposeAsync(); } } - + [CollectionDefinition("Postgres collection")] public class PostgresCollection : ICollectionFixture - { + { } -} +} \ No newline at end of file diff --git a/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs b/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs index 0cc13c8b2..4faba0af2 100644 --- a/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs +++ b/test/WorkflowCore.Tests.Redis/RedisDockerSetup.cs @@ -4,7 +4,7 @@ using Xunit; namespace WorkflowCore.Tests.Redis -{ +{ public class RedisDockerSetup : IAsyncLifetime { private readonly RedisResource _redisResource; @@ -29,6 +29,6 @@ public Task DisposeAsync() [CollectionDefinition("Redis collection")] public class RedisCollection : ICollectionFixture - { + { } -} +} \ No newline at end of file diff --git a/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs b/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs index 147c09241..cf50f854d 100644 --- a/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs +++ b/test/WorkflowCore.Tests.SqlServer/DockerSetup.cs @@ -31,6 +31,6 @@ public Task DisposeAsync() [CollectionDefinition("SqlServer collection")] public class SqlServerCollection : ICollectionFixture - { + { } } \ No newline at end of file From 286b9fef42a419347b0d38ee407f798782ca8f67 Mon Sep 17 00:00:00 2001 From: glucaci Date: Fri, 25 Nov 2022 11:51:12 +0100 Subject: [PATCH 440/462] Enable no table scan and fix one more index --- .../Services/MongoPersistenceProvider.cs | 6 ++++-- test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 0d238ec64..551167add 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -112,8 +112,10 @@ static void CreateIndexes(MongoPersistenceProvider instance) instance.EventSubscriptions.Indexes.CreateOne(new CreateIndexModel( Builders.IndexKeys .Ascending(x => x.EventName) - .Ascending(x => x.EventKey), - new CreateIndexOptions { Background = true, Name = "idx_namekey" })); + .Ascending(x => x.EventKey) + .Ascending(x => x.SubscribeAsOf) + .Ascending(x => x.ExternalToken), + new CreateIndexOptions { Background = true, Name = "idx_namekey_v2" })); instance.ScheduledCommands.Indexes.CreateOne(new CreateIndexModel( Builders.IndexKeys diff --git a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs index 7b956196d..fddf108c6 100644 --- a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs +++ b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs @@ -19,9 +19,8 @@ public MongoDockerSetup() public async Task InitializeAsync() { await _mongoResource.InitializeAsync(); - // TODO: Should be enabled - // var command = new BsonDocument { { "setParameter", 1 }, { "notablescan", 1 } }; - // _mongoResource.Client.GetDatabase("admin").RunCommand(command); + var command = new BsonDocument { { "setParameter", 1 }, { "notablescan", 1 } }; + _mongoResource.Client.GetDatabase("admin").RunCommand(command); ConnectionString = _mongoResource.ConnectionString; } From 5ae4acb2976cb16e3967edde9cc63b00a2bd2cfa Mon Sep 17 00:00:00 2001 From: glucaci Date: Fri, 25 Nov 2022 12:16:43 +0100 Subject: [PATCH 441/462] Try fix index --- .../Services/MongoPersistenceProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 551167add..034e4e402 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -111,8 +111,8 @@ static void CreateIndexes(MongoPersistenceProvider instance) instance.EventSubscriptions.Indexes.CreateOne(new CreateIndexModel( Builders.IndexKeys - .Ascending(x => x.EventName) .Ascending(x => x.EventKey) + .Ascending(x => x.EventName) .Ascending(x => x.SubscribeAsOf) .Ascending(x => x.ExternalToken), new CreateIndexOptions { Background = true, Name = "idx_namekey_v2" })); From ae94ecb82a5961c2e3af14f98b71139fb84021e8 Mon Sep 17 00:00:00 2001 From: glucaci Date: Fri, 25 Nov 2022 15:15:48 +0100 Subject: [PATCH 442/462] Fix index --- .../Services/MongoPersistenceProvider.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 034e4e402..0f9fca3cf 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -112,10 +112,16 @@ static void CreateIndexes(MongoPersistenceProvider instance) instance.EventSubscriptions.Indexes.CreateOne(new CreateIndexModel( Builders.IndexKeys .Ascending(x => x.EventKey) - .Ascending(x => x.EventName) - .Ascending(x => x.SubscribeAsOf) - .Ascending(x => x.ExternalToken), + .Ascending(x => x.EventName), new CreateIndexOptions { Background = true, Name = "idx_namekey_v2" })); + + instance.EventSubscriptions.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys.Ascending(x => x.SubscribeAsOf), + new CreateIndexOptions { Background = true, Name = "idx_subscribe" })); + + instance.EventSubscriptions.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys.Ascending(x => x.ExternalToken), + new CreateIndexOptions { Background = true, Name = "idx_token" })); instance.ScheduledCommands.Indexes.CreateOne(new CreateIndexModel( Builders.IndexKeys From 53143d410ac08e503db7962dc876482e23f69f30 Mon Sep 17 00:00:00 2001 From: glucaci Date: Sat, 26 Nov 2022 09:58:40 +0100 Subject: [PATCH 443/462] Complete index --- .../Services/MongoPersistenceProvider.cs | 17 +++++------------ .../MongoDockerSetup.cs | 2 -- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index 0f9fca3cf..fe43589b6 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -95,7 +95,8 @@ static void CreateIndexes(MongoPersistenceProvider instance) instance.WorkflowInstances.Indexes.CreateOne(new CreateIndexModel( Builders.IndexKeys .Ascending(x => x.NextExecution) - .Ascending(x => x.Status), + .Ascending(x => x.Status) + .Ascending(x => x.Id), new CreateIndexOptions {Background = true, Name = "idx_nextExec_v2"})); instance.Events.Indexes.CreateOne(new CreateIndexModel( @@ -111,17 +112,9 @@ static void CreateIndexes(MongoPersistenceProvider instance) instance.EventSubscriptions.Indexes.CreateOne(new CreateIndexModel( Builders.IndexKeys - .Ascending(x => x.EventKey) - .Ascending(x => x.EventName), - new CreateIndexOptions { Background = true, Name = "idx_namekey_v2" })); - - instance.EventSubscriptions.Indexes.CreateOne(new CreateIndexModel( - Builders.IndexKeys.Ascending(x => x.SubscribeAsOf), - new CreateIndexOptions { Background = true, Name = "idx_subscribe" })); - - instance.EventSubscriptions.Indexes.CreateOne(new CreateIndexModel( - Builders.IndexKeys.Ascending(x => x.ExternalToken), - new CreateIndexOptions { Background = true, Name = "idx_token" })); + .Ascending(x => x.EventName) + .Ascending(x => x.EventKey), + new CreateIndexOptions { Background = true, Name = "idx_namekey" })); instance.ScheduledCommands.Indexes.CreateOne(new CreateIndexModel( Builders.IndexKeys diff --git a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs index fddf108c6..df281ce29 100644 --- a/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs +++ b/test/WorkflowCore.Tests.MongoDB/MongoDockerSetup.cs @@ -19,8 +19,6 @@ public MongoDockerSetup() public async Task InitializeAsync() { await _mongoResource.InitializeAsync(); - var command = new BsonDocument { { "setParameter", 1 }, { "notablescan", 1 } }; - _mongoResource.Client.GetDatabase("admin").RunCommand(command); ConnectionString = _mongoResource.ConnectionString; } From e00d53bb4d4cb144bd484fd894e81e93bf1b4116 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 27 Nov 2022 17:42:21 -0800 Subject: [PATCH 444/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 298e3079c..a56e32d2d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.7.0 - 3.7.0.0 - 3.7.0.0 + 3.8.0 + 3.8.0.0 + 3.8.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.7.0 + 3.8.0 From 8fce34fa216d2630758eb34891b4f37c1e399ab3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 01:53:51 +0000 Subject: [PATCH 445/462] Bump System.Data.SqlClient Bumps [System.Data.SqlClient](https://github.com/dotnet/corefx) from 4.6.1 to 4.8.5. - [Release notes](https://github.com/dotnet/corefx/releases) - [Commits](https://github.com/dotnet/corefx/commits) --- updated-dependencies: - dependency-name: System.Data.SqlClient dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../WorkflowCore.QueueProviders.SqlServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj index 77d5e2421..c24b7d89e 100644 --- a/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.QueueProviders.SqlServer/WorkflowCore.QueueProviders.SqlServer.csproj @@ -22,7 +22,7 @@ - + From 5edfa3087062bdc48f24da8640dc7bc23418feb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 01:54:11 +0000 Subject: [PATCH 446/462] Bump System.Data.SqlClient Bumps [System.Data.SqlClient](https://github.com/dotnet/corefx) from 4.6.1 to 4.8.5. - [Release notes](https://github.com/dotnet/corefx/releases) - [Commits](https://github.com/dotnet/corefx/commits) --- updated-dependencies: - dependency-name: System.Data.SqlClient dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../WorkflowCore.LockProviders.SqlServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj b/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj index 77fbe950d..5c798129a 100644 --- a/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj +++ b/src/providers/WorkflowCore.LockProviders.SqlServer/WorkflowCore.LockProviders.SqlServer.csproj @@ -8,7 +8,7 @@ - + From 2fe3916859a14b0b4d9cdbbd6e0a8681d32812b1 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 28 Nov 2022 08:54:36 +0100 Subject: [PATCH 447/462] Add mysql to PR build --- .github/workflows/dotnet.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 8bb2947f6..83afb451b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -55,6 +55,22 @@ jobs: run: dotnet build --no-restore - name: MongoDB Tests run: dotnet test test/WorkflowCore.Tests.MongoDB --no-build --verbosity normal -p:ParallelizeTestCollections=false + MySQL-Tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: | + 3.1.x + 6.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: MySQL Tests + run: dotnet test test/WorkflowCore.Tests.MySQL --no-build --verbosity normal -p:ParallelizeTestCollections=false PostgreSQL-Tests: runs-on: ubuntu-latest steps: From 136238445ca1c7f61aabeb9715092b5acb94faf9 Mon Sep 17 00:00:00 2001 From: glucaci Date: Mon, 28 Nov 2022 11:52:54 +0100 Subject: [PATCH 448/462] Fix mysql tests --- test/WorkflowCore.Tests.MySQL/DockerSetup.cs | 8 +++++--- .../WorkflowCore.Tests.MySQL.csproj | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs index 9444a8005..06c041818 100644 --- a/test/WorkflowCore.Tests.MySQL/DockerSetup.cs +++ b/test/WorkflowCore.Tests.MySQL/DockerSetup.cs @@ -19,8 +19,10 @@ public MysqlDockerSetup() public async Task InitializeAsync() { await _mySqlResource.InitializeAsync(); - ConnectionString = _mySqlResource.ConnectionString; - ScenarioConnectionString = _mySqlResource.ConnectionString; + var workflowConnection = await _mySqlResource.CreateDatabaseAsync("workflow"); + ConnectionString = workflowConnection.ConnectionString; + var scenariosConnection = await _mySqlResource.CreateDatabaseAsync("scenarios"); + ScenarioConnectionString = scenariosConnection.ConnectionString; } public Task DisposeAsync() @@ -28,7 +30,7 @@ public Task DisposeAsync() return _mySqlResource.DisposeAsync(); } } - + [CollectionDefinition("Mysql collection")] public class MysqlCollection : ICollectionFixture { diff --git a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj index cfdb4163d..a56acc9b7 100644 --- a/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj +++ b/test/WorkflowCore.Tests.MySQL/WorkflowCore.Tests.MySQL.csproj @@ -1,7 +1,7 @@ - + From 6a7ae386b1e200ad638b0db026a3bc614d4f2995 Mon Sep 17 00:00:00 2001 From: glucaci Date: Wed, 7 Dec 2022 14:54:07 +0100 Subject: [PATCH 449/462] Fix persist workflow when has no subscription --- .../Services/BackgroundTasks/WorkflowConsumer.cs | 2 +- .../Services/MongoPersistenceProvider.cs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs index 3a6465902..b092f41f6 100644 --- a/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs +++ b/src/WorkflowCore/Services/BackgroundTasks/WorkflowConsumer.cs @@ -55,7 +55,7 @@ protected override async Task ProcessItem(string itemId, CancellationToken cance finally { WorkflowActivity.Enrich(result); - await _persistenceStore.PersistWorkflow(workflow, result.Subscriptions, cancellationToken); + await _persistenceStore.PersistWorkflow(workflow, result?.Subscriptions, cancellationToken); await QueueProvider.QueueWork(itemId, QueueType.Index); _greylist.Remove($"wf:{itemId}"); } diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs index fe43589b6..fdab92cf6 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Persistence.MongoDB/Services/MongoPersistenceProvider.cs @@ -154,12 +154,18 @@ public async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken c public async Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default) { - using (var session = await _database.Client.StartSessionAsync()) + if (subscriptions == null || subscriptions.Count < 1) + { + await PersistWorkflow(workflow, cancellationToken); + return; + } + + using (var session = await _database.Client.StartSessionAsync(cancellationToken: cancellationToken)) { session.StartTransaction(); await PersistWorkflow(workflow, cancellationToken); await EventSubscriptions.InsertManyAsync(subscriptions, cancellationToken: cancellationToken); - await session.CommitTransactionAsync(); + await session.CommitTransactionAsync(cancellationToken); } } From 86ed4fad656286568417f0cf3e82fc1a4be1aa3f Mon Sep 17 00:00:00 2001 From: glucaci Date: Wed, 7 Dec 2022 14:59:04 +0100 Subject: [PATCH 450/462] Update version --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index a56e32d2d..8793e7e27 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.8.0 - 3.8.0.0 - 3.8.0.0 + 3.8.1 + 3.8.1.0 + 3.8.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.8.0 + 3.8.1 From a9a4e5c59c976ce6dbfc3295b73e1b3f1ade957f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Dec 2022 22:11:10 +0000 Subject: [PATCH 451/462] Bump Newtonsoft.Json in /src/samples/WebApiSample/WebApiSample Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 13.0.1 to 13.0.2. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/13.0.1...13.0.2) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/samples/WebApiSample/WebApiSample/WebApiSample.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj b/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj index 133f705b4..e1454eb7d 100644 --- a/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj +++ b/src/samples/WebApiSample/WebApiSample/WebApiSample.csproj @@ -14,7 +14,7 @@ - + From b5e141f4b798ddcd85cc3a0495d57e6dbfe4b796 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Mar 2023 23:26:00 +0000 Subject: [PATCH 452/462] Bump MongoDB.Driver in /test/WorkflowCore.Tests.MongoDB Bumps [MongoDB.Driver](https://github.com/mongodb/mongo-csharp-driver) from 2.8.1 to 2.19.0. - [Release notes](https://github.com/mongodb/mongo-csharp-driver/releases) - [Commits](https://github.com/mongodb/mongo-csharp-driver/compare/v2.8.1...v2.19.0) --- updated-dependencies: - dependency-name: MongoDB.Driver dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../WorkflowCore.Persistence.MongoDB.csproj | 2 +- .../WorkflowCore.Tests.MongoDB.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj index 5d395eee9..e55685a2e 100644 --- a/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj +++ b/src/providers/WorkflowCore.Persistence.MongoDB/WorkflowCore.Persistence.MongoDB.csproj @@ -22,7 +22,7 @@ - + diff --git a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj index 68251d271..c708f7bf3 100644 --- a/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj +++ b/test/WorkflowCore.Tests.MongoDB/WorkflowCore.Tests.MongoDB.csproj @@ -21,7 +21,7 @@ - + From e7219e5d3025b144c08e6f8ab74c0863373e289e Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 6 Apr 2023 07:34:08 -0700 Subject: [PATCH 453/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 8793e7e27..a617157cb 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.8.1 - 3.8.1.0 - 3.8.1.0 + 3.8.2 + 3.8.2.0 + 3.8.2.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.8.1 + 3.8.2 From b06b690691d47222075c57ecc0f6a3394b27b781 Mon Sep 17 00:00:00 2001 From: Stuart McKenzie Date: Thu, 27 Apr 2023 14:54:17 +1000 Subject: [PATCH 454/462] add alternate constuctor arguments to allow supply of provisioned sqs and dynamo clients --- .../WorkflowCore.Providers.AWS/README.md | 12 +++++++++ .../ServiceCollectionExtensions.cs | 27 ++++++++++++++++--- .../Services/DynamoDbProvisioner.cs | 4 +-- .../Services/DynamoLockProvider.cs | 4 +-- .../Services/DynamoPersistenceProvider.cs | 4 +-- .../Services/SQSQueueProvider.cs | 4 +-- .../DynamoPersistenceProviderFixture.cs | 4 +-- 7 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.AWS/README.md b/src/providers/WorkflowCore.Providers.AWS/README.md index f9c000662..0a9286f37 100644 --- a/src/providers/WorkflowCore.Providers.AWS/README.md +++ b/src/providers/WorkflowCore.Providers.AWS/README.md @@ -34,6 +34,18 @@ services.AddWorkflow(cfg => If any AWS resources do not exists, they will be automatcially created. By default, all DynamoDB tables and indexes will be provisioned with a throughput of 1, you can modify these values from the AWS console. You may also specify a prefix for the dynamo table names. +If you have a preconfigured dynamoClient, you can pass this in instead of the credentials and config +```C# +var client = new AmazonDynamoDBClient(); +var sqsClient = new AmazonSQSClient(); +services.AddWorkflow(cfg => +{ + cfg.UseAwsDynamoPersistenceWithProvisionedClient(client, "table-prefix"); + cfg.UseAwsDynamoLockingWithProvisionedClient(client, "workflow-core-locks"); + cfg.UseAwsSimpleQueueServiceWithProvisionedClient(sqsClient, "queues-prefix"); +}); +``` + ## Usage (Kinesis) diff --git a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs index 57b6f6bc8..171860524 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs @@ -15,20 +15,39 @@ public static class ServiceCollectionExtensions { public static WorkflowOptions UseAwsSimpleQueueService(this WorkflowOptions options, AWSCredentials credentials, AmazonSQSConfig config, string queuesPrefix = "workflowcore") { - options.UseQueueProvider(sp => new SQSQueueProvider(credentials, config, sp.GetService(), queuesPrefix)); + options.UseQueueProvider(sp => new SQSQueueProvider(credentials, config, null, sp.GetService(), queuesPrefix)); + return options; + } + + public static WorkflowOptions UseAwsSimpleQueueServiceWithProvisionedClient(this WorkflowOptions options, AmazonSQSClient sqsClient, string queuesPrefix = "workflowcore") + { + options.UseQueueProvider(sp => new SQSQueueProvider(null, null, sqsClient, sp.GetService(), queuesPrefix)); return options; } public static WorkflowOptions UseAwsDynamoLocking(this WorkflowOptions options, AWSCredentials credentials, AmazonDynamoDBConfig config, string tableName) { - options.UseDistributedLockManager(sp => new DynamoLockProvider(credentials, config, tableName, sp.GetService(), sp.GetService())); + options.UseDistributedLockManager(sp => new DynamoLockProvider(credentials, config, null, tableName, sp.GetService(), sp.GetService())); + return options; + } + + public static WorkflowOptions UseAwsDynamoLockingWithProvisionedClient (this WorkflowOptions options, AmazonDynamoDBClient dynamoClient, string tableName) + { + options.UseDistributedLockManager(sp => new DynamoLockProvider(null, null, dynamoClient, tableName, sp.GetService(), sp.GetService())); return options; } public static WorkflowOptions UseAwsDynamoPersistence(this WorkflowOptions options, AWSCredentials credentials, AmazonDynamoDBConfig config, string tablePrefix) { - options.Services.AddTransient(sp => new DynamoDbProvisioner(credentials, config, tablePrefix, sp.GetService())); - options.UsePersistence(sp => new DynamoPersistenceProvider(credentials, config, sp.GetService(), tablePrefix, sp.GetService())); + options.Services.AddTransient(sp => new DynamoDbProvisioner(credentials, config, null, tablePrefix, sp.GetService())); + options.UsePersistence(sp => new DynamoPersistenceProvider(credentials, config, null, sp.GetService(), tablePrefix, sp.GetService())); + return options; + } + + public static WorkflowOptions UseAwsDynamoPersistenceWithProvisionedClient(this WorkflowOptions options, AmazonDynamoDBClient dynamoClient, string tablePrefix) + { + options.Services.AddTransient(sp => new DynamoDbProvisioner(null, null, dynamoClient, tablePrefix, sp.GetService())); + options.UsePersistence(sp => new DynamoPersistenceProvider(null, null, dynamoClient, sp.GetService(), tablePrefix, sp.GetService())); return options; } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs index 3f381c8c3..6d3c712b7 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs @@ -15,10 +15,10 @@ public class DynamoDbProvisioner : IDynamoDbProvisioner private readonly IAmazonDynamoDB _client; private readonly string _tablePrefix; - public DynamoDbProvisioner(AWSCredentials credentials, AmazonDynamoDBConfig config, string tablePrefix, ILoggerFactory logFactory) + public DynamoDbProvisioner(AWSCredentials credentials, AmazonDynamoDBConfig config, AmazonDynamoDBClient dynamoDBClient, string tablePrefix, ILoggerFactory logFactory) { _logger = logFactory.CreateLogger(); - _client = new AmazonDynamoDBClient(credentials, config); + _client = dynamoDBClient ?? new AmazonDynamoDBClient(credentials, config); _tablePrefix = tablePrefix; } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs index 6f4aca0e8..32ebe488b 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -25,10 +25,10 @@ public class DynamoLockProvider : IDistributedLockProvider private readonly AutoResetEvent _mutex = new AutoResetEvent(true); private readonly IDateTimeProvider _dateTimeProvider; - public DynamoLockProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, string tableName, ILoggerFactory logFactory, IDateTimeProvider dateTimeProvider) + public DynamoLockProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, AmazonDynamoDBClient dynamoDBClient, string tableName, ILoggerFactory logFactory, IDateTimeProvider dateTimeProvider) { _logger = logFactory.CreateLogger(); - _client = new AmazonDynamoDBClient(credentials, config); + _client = dynamoDBClient ?? new AmazonDynamoDBClient(credentials, config); _localLocks = new List(); _tableName = tableName; _nodeId = Guid.NewGuid().ToString(); diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index 09f1dbc4c..0c78c6048 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -26,10 +26,10 @@ public class DynamoPersistenceProvider : IPersistenceProvider public bool SupportsScheduledCommands => false; - public DynamoPersistenceProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, IDynamoDbProvisioner provisioner, string tablePrefix, ILoggerFactory logFactory) + public DynamoPersistenceProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, AmazonDynamoDBClient dynamoDBClient, IDynamoDbProvisioner provisioner, string tablePrefix, ILoggerFactory logFactory) { _logger = logFactory.CreateLogger(); - _client = new AmazonDynamoDBClient(credentials, config); + _client = dynamoDBClient ?? new AmazonDynamoDBClient(credentials, config); _tablePrefix = tablePrefix; _provisioner = provisioner; } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs index dd1c15e14..af2c40b20 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs @@ -21,10 +21,10 @@ public class SQSQueueProvider : IQueueProvider public bool IsDequeueBlocking => true; - public SQSQueueProvider(AWSCredentials credentials, AmazonSQSConfig config, ILoggerFactory logFactory, string queuesPrefix) + public SQSQueueProvider(AWSCredentials credentials, AmazonSQSConfig config, AmazonSQSClient sqsClient, ILoggerFactory logFactory, string queuesPrefix) { _logger = logFactory.CreateLogger(); - _client = new AmazonSQSClient(credentials, config); + _client = sqsClient ?? new AmazonSQSClient(credentials, config); _queuesPrefix = queuesPrefix; } diff --git a/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs index 6fb9c1ea9..6ec1c13b6 100644 --- a/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs @@ -26,8 +26,8 @@ protected override IPersistenceProvider Subject if (_subject == null) { var cfg = new AmazonDynamoDBConfig { ServiceURL = DynamoDbDockerSetup.ConnectionString }; - var provisioner = new DynamoDbProvisioner(DynamoDbDockerSetup.Credentials, cfg, "unittests", new LoggerFactory()); - var client = new DynamoPersistenceProvider(DynamoDbDockerSetup.Credentials, cfg, provisioner, "unittests", new LoggerFactory()); + var provisioner = new DynamoDbProvisioner(DynamoDbDockerSetup.Credentials, cfg, null, "unittests", new LoggerFactory()); + var client = new DynamoPersistenceProvider(DynamoDbDockerSetup.Credentials, cfg, null, provisioner, "unittests", new LoggerFactory()); client.EnsureStoreExists(); _subject = client; } From 1820211a7a554509dda9f4370e21495281594e03 Mon Sep 17 00:00:00 2001 From: Peeyush Chandel <555114+cpeeyush@users.noreply.github.com> Date: Thu, 27 Apr 2023 08:42:50 +0200 Subject: [PATCH 455/462] Add performance test results under doc section --- .../performance-test-workflows-latency.png | Bin 0 -> 119222 bytes .../performance-test-workflows-per-second.png | Bin 0 -> 80363 bytes docs/performance.md | 87 ++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 docs/images/performance-test-workflows-latency.png create mode 100644 docs/images/performance-test-workflows-per-second.png create mode 100644 docs/performance.md diff --git a/docs/images/performance-test-workflows-latency.png b/docs/images/performance-test-workflows-latency.png new file mode 100644 index 0000000000000000000000000000000000000000..621fc276012896c488076675032629229042634f GIT binary patch literal 119222 zcmeEv30PCt+HTaUZ5=qRRvZwjZNUMF3KCEt&J;1AQblA^Q4kPo<|9Lui+csZ&=@2 zdvooOh3TSiSAGkF!4{eQy7veS_8kQVTY&v~4!8nW;OfDTS>8uXcf&HP)mY${uUvMa zcEMmTq83O`&4s~c&p&kVw|&jc%}q^BEiEmrt*zky$&cpVfF0Jq8y&xFav}_Adllsp zZh7w7k@JzidET_Kv9ZNB*n7u2I5;>tIk~#Jy8li%pIqnhnC9u}iB4_sN^A5=Yw~*1 z>h;{z+uPeW@pMNC)W;_UWv=I56!p#J+u1v0*A}sljnXVyY@)Qu6vE<4r|lX)&p^oca!Y8psv39hH>)%F4>B+QzE-@)`!awzjsu=R*Vd zbksJsGaH$FASfWQHb!6D;K%mrxAZqz^v2>&@EI2N^z`%&jPwpOdO7WV75RNl)V{vH zen$DgUE9Gw_78#&S3GphbSTzgh|L`y9%hGZVUG`t+&I9AH0R(?b2yw)@DWJ3NoTpG zx?JH1ujwuSSpdIm8=u)eo_}Nfo&NavxL~AF_}*Zm!cg>PrRc+Uap@{Cy;3X|OWvwU zs*EJeW@*`KDb-lU>67t><>eZR5}4xskBZuzipJxLb}t2oqTtmigbanKU!hPmw?tF| zh|KpnVqx{`$$(f!ztI0;dknbrjn}U>-Z0qK66oKob!Ser!C*3++1_2h1vm_MFe20( zah?A2^(&~JN5-n`s!(TMUk}^7DobDeweHtle>iVxwg2UI@>h2Qbnd>swt1J0*FP#t zy6w)~h|LZN*M5B&rSs;p#{hDxeVT>h$%r^%M19;; z!u8{30csu`!}T*HkvJssE9kfVir47g`QRI@{wW*b1brEN%uxQ0qCoAR;IWW7974$E zfgcY%#f!j?ogMsD;72Htg#bTbzBDxSV=(pajzA3mv2ap7(Vp1e@aHLIj*mELC~v>2 z9bK`bh<3Ld2rU$^TL%s+`R*ec7CP*$yXC`zjS;3pRy%IOF{jL9qhpiQ#?C7WrHjn- zSM^ZgC!=7aMnDONqBsoh?r4z|BDkdr>kJq`z|>O0@h$;2@B5=Gs4nE9E8y0Nx*GY&B5)%`l8E5W)#uWpSr}plEG?GxV>F` ze&2GM#`swWSZL2f2F&Yj>k3<*A>3$*(De{iNPacHl5l(&AyEX9xD(iDi-w8&s+z53 zZi`8g@*gcfqOr_hU)erEJda+TP@BhI&d9H!R4;&e=>e?XJhEd>P^!#1s;OTa*R3gI zew}q9$cKVCS5sg^4!R)OpEr)@YV>|1p*r2XBIqZ_1XJ0IzN{q?GA7IsZK~dZF3yMBfze2Imil(`%L`Yx0ix7#KewZJ z)QYk)epip@!Q690TZ-KrJGI`C>3K&u^Ca<4mn-O z7*-6i-zQ5@#9HUEb?&i4EJP*l)jm zulE75J57rHoBP-tcQL94B_Ud4*0@>^AfjT5tKQVSuxt;JiiYmzYd8`8D(C(NQ(GaM z7n)pOP2uD1Y3M+D7CF=YA;YM(Qcy{*UO>u`EvJRq>O7d;`F@fvc?x4V9!vA47mwIF z{Mz~hU?hd+K<^LhXu;lawJZ$%HuR@trRGJk5DC;rd|g@^<{>Q;NbhaEyfv)^SpcI_2+4M`$Y*}4vPA$Ip4+ehtBuJjvc_3ldd_be;$S7poG9PO|t zh+E#Vd*eDD^G5ba(#u_93G-dBLdQ%^yrqr~vV*ye;GAXWB|N1k-Wn z3n~F8kvH_RRMGt$U&Ygqa8-0B?_$0Z*8JYaagvW&RYZX!_xju}`EbU_^EeF+$27+P zY$4Hu7IKj%=sv@pXYJS)b_r`G?zYlsbFpKFS&&kj(cd~6i^j$}dff?*WDnult+*3j z_4&@F#XYQm zGMI9B*3Q03l*_Jb)*&-Bt%PyW{wkOdp+<*l;z2}Yxntc8ogfDO$5d9=nXRH*BjI-v zo@0F^R@-%B8Er>dU){WCb2|R4ip6PUt}UGzhC=##ilvhJjdu{39|@W{qt*M+=k^em z3-BpgQRRrImwc*Cdhgks!sX8sG}brinG=Z8+t&X2ZCVT_daS!`CuM!eSRwzVyupO^ z!x-biM1$4{kx?^(i7VJ=>algTz!zH`k?Yr`^1XUn4y7x+r47!`W(6VT*{(mxA9%Cw z-(MVXh_62+tUf}q@s<{OTBC6Xmci8SVCR$^K0tf<`WpX@o?cL#LJ}l4d{|tOyRFdv zer-YZGOY)g-%0b&GHiwyRS=^)@u5Dn3?@rdQC{0?FLHgBA$&|>D(X%>+A<`0J{m*ybN3RALrg)@SXL zmi*S#qi&I8`*ZYjK7O=$B*{DHx0}oSn>%$n2D8`8b_H?O|CoJzLLWbzSeBJDGJ%(G ztODLuO^LhsyNjyl&|;X>fl4a1=`c~X;{5k=LH8q4*t?3}T^nmy9HNzq&VGc@ACc_2 zviQ*PI{v6tJfk>Z2l0c;)`WvK9j>V8l5~H^{b(ZM97AFoSdV1<7?L8-)3P$>)h;qA zaOo0d(W6==lw1>k-YBmixia`_8A&R=_RzB3>qVQZi-y*c9u3DV3su4^XWafE_ihtQ zvSYEr$GfAW=IrT*`r0?Pv}!qWi3Q0Y+dP8;#TksH#Q~PW@}csATsH$`hHMa7*JL2k zGhB`~@iR^_o=2)2s_Py-+;*U}`dVK@FOE2rwoLKk#}|o7yhpjE%lueBso>vexy zG=K;s4((0Vtq%9`*vH!fXR=OhWElKRsLI}as>)l)Lhbf!<;|^YOJQB)_+KY2`9#shi7g$W%rc*2!N~3SwCmd>y6v_- z7yNPeX{?G>{u{l=J-=GgWr7*dF+3_Ol0!49TF1YuP@N zt(-cOcna;zL!bK8Qw4jBUHyBaR635@o8t(toq}YG@y0zL!%4TVSU9D6=}(rLdYh_lmovJ>57rjwYJtj-1DQ#kvw19k${u zk@{YWY_`;Tl@sI{Y`Nq;O;qkv^C~`TBDgitm)4PeW^DLYuCwb+<2*eTdqrUgt)Ei8 zq%*wbtX59CDUl@CNqL$Aah-VTrO(j=E;%y8>;mq%Za~$Xu=6{1goL4g2@`fVl&a zVd;Cg>(z2tZb_ZRNtQ6uH@(vO>wKx)Rlfs%#UHWJAwe@WXY*A{i{(knR89OVKJe0- zk`roXAMrQsv&OhiEJ4_vFE=W}9by0*<~$#{=R)J(@%iD5Kxn(7=YxqE2Wzt6r<^mJ z+xo{;Ip$q1CgR8T+k%^)%Ldoa-;^*I{b={nf5b$!8V#qAUTMoh4#-loOOGYJV4t#@ zxSaWl;BdTA4<1{MZIOD+6IKT!GbF-<$Gn%z2w%Y(Kr(M|fIaMKIG1AXLJYcb&}T_|EN_KZ(#~pnE@{(HXW4QV&4r^^6zql_+*t$s5xZZ9m4tgS zD%Fc-Q^mC25EH4=O6?6;as;F*jR1o6V6|^Q6M{e0<%mdgC78Uf*;fe^g0n zfE(D7skQXDki_3z$6zz!s>-^!BkX6dOFd=Qmgs`f zD;jL>8W?vKa!_U*1{@AkMVs2(25kO7S( zW>5Td40gy^86q^#tQyCsn2ijoA*j7EOdc!nHmmO^b5896@3$?_^wz8{UYbhco-t`S z80)dr@ulF_d4sud-q2p;AW~F=UdT@xN*JVkyCfXC`H1pxqC>(IN!#%-0H6-DA({s zMoyKbsa_K*5+k^Q7B8`|tW7(lPu|^%t1;XDZJ?_Atex!1q(st+qF{cFjn=mRcz0_h zf8MI}CGGNOBirLdY=gkMr?Yb$w;YHT$XnJdxK67(bF!g*@STsq61|q*amz(NdJ{x- z#FDg)O{9!A!Wey}9Q9yim8GIS-O296J?BC5OBoFGBY3g4u?H)$j(Dlm^Kw>FrpTsw zQxp5*@_&55T|19qUzIttgV6rXOH~vlyI;rAvjXr1>myIfDE&XLUed*~gdO^6GLtR# zqoIkI9+czu#M9gT9TPhR*1iRHHm_{oKN@+rj^=*2ZYZXDah$FAfiHn{-q%U&Ec+&e zwJgp4X2a<&;I3kVq7SNqgy!41zrYY_=C;pt{^T(p+?jEBJR_|>m z-giI!&D}2~pX_HXb0(E)$}h**rR{liPgtXmwp#gG3*FIZDS#2mW23aZ#)mh$N0@VMt$zt?zy(OuiImL z!`bd(S$A_2yCwkbEBy9E%N>h`W^zIGbN^U})ZA(xT!(;yPEX zM%ReW8(HmAjl4OfNyi<6?qjV?q)r6dTsM?=-8Ot4I;V?nqu>~tWr4-2!3M|^u1CFp zg!L}kTsc(LAGp!GeWWpCPpYu{xKfPBglN@cj~UlY*i1I-OhR;+qnPzcjy=%={8*I8 zvpAlBc;ShA7qrpCMs}9NUr&p&U+siDiU=Y^X64kQe}@;JN#~*UCkBP5A3sk;XM07&X?TNcoOUC;rrO4-HUPdM;4YoJJ<3{sHX2sIrs(ZT{oR z#8eFucXiA$k9zb~%EZguJViuj*QtUEYtHPlbiol~_>j-q#^IO5Yuv*-HV&hWlt(kh#hA@%({302rqFYVj6(Y|ymVd#0)1zN}%{KxB- zIp&7Pv|h=MvOe6OsJi6S(>fA+o#t;NwPzNmsGJZMRvtr`T%fFo;vlO%_e$1svXrjs zb&MW}$g!6=wYCkwg^-#u`S{nsU{;&Tti!@PG#zCo_FJ}HNP?@KkQO}yL9irSE2c?3 zm62g6ji{qUdLr-Ao$*RSxv3#B48rWrC(8Alp4qBFgs_7wmGjkKzT;$c~@ub-LGC(F^3(+cwCtlbG_tIU2d*!wwtGKkBOLi=KG||y+Vn} zkidmpK-^TB75uAvn(#aIiN&&`>^JcYLOcsMc9zu}m(rD*rlRJzDtH_)4IICNwme~+ znCutO1kW-)2$nPxu_?G!EWP`0+Ro?sH!6ZG0%P-Ml^m{xVz28R{mr*WPFLW`^(W#X z<&z}!Iu@z1&n>(p>BQ)--#FeI#2OM4lT&G1zSXUqA`mO4@*8zJS{6={7boOyB!GUk|sbM~fYUTWS{~jYB%K+d9(=9EaVcG8BUgI<#2v z$j2Eqh!|@-U!bI~Za&nH@}0n3;53nl)kWUDROc8GlHH@cT!b#7>>(2N3$# z`hRD=d|J_q2q{p-^xc}Guvm=pcRpOabutHtPlMxj65$_Q9hQrM09hGvO_((i5)=nS zAFY1!hXpnyx6$0JJV)cagb`w9+r6QTM)kZ>5*EWgC8iQfDZOio^38R-+RhjHH_C%7 z>NGNh5x>C>?fBD^U)8MZpgJ$Z9XaQk8LLAZkkxq;qg`3C=Kwa%)9a3I5ox^%f4rhE ze8ecB?(PeNO{htqX-i>MBUzr?Un>1j;@C?mZ;*8*-saW{NYAXBuUMXYbUQlG;Sy@M zs^cBH|FWCgSuJ}>4}q@9bk8lV{2v&2)4i1)f(az6in&ra5DWbOABilXz}PF*PR0 zg-z){CQSD-Z)*AE+T*c{4nC%>|GLP`pNM%EoY{nG=IySt-(z+E15kVHv6N_o7@by| zglD*O2+BYz?{(DbU&>rvwMNtM=zsH+!;v}ne-z3{e8X|Ogmojik4HW7 z+jqDtj0=b)_Fegw**kqF*Lx7TB*g>DX1IAYC9KfFKj=nT#1{1<*6XG7C3#kzBPLq# zWgAPW!`z zrxPua1mR^8fAL|8jT8hX$_d5(4XzD`C^nJ?8ln4)Ui-FBKp1>c^m_5CfOj$B01!B4 znW!%ETHSn<|KSOJ+Yu-gw3VfEVV&zH%TMBMdbdYBpKMV)jUPe2gIieA%Jk4c&N4DJ zU5>kn5|e5FR9Tk8WPvAZ968e$e5++Wx8s0tDRGK z_$pZ-zR57eGrC14?8303{O#{vqg%-F&Y_%+76cD#1ww1x?K=2kc6=A53#_(R-$!`| zehk!I8B!P*dmUf}c-v!*nd)u$k!-qdzVZ3S)pDz7t)k;bb6J}AVp?==(w*Ciz>3>F zrC+;4^BdTPG^j#Wx0HP`cVJJvgCC_fA#FhD8ffxjGk!SfR%!!-R52ccC_^UJjCvxA zjvGXsk}Z9;W6uLaj)_2EzoW}8co*@qX*$j+=Bzn~DEF3tsBwydF^*ef>EFD^pH-=M zRGDcJIhy=5n!mVi6TB+Ny8IP+Fmu5#s;)PL$$smf#|iLn0QzSiq+ETRrn1^^h!W@@ zcC@B1Ljc_J{Hw+PCPnB6^8YHWdPH$CZYHsEdKKwW~P-N95$$3d=bApJ~7 z+sD|f{M;&1>UUoxz-&*Hl$iMbu?l@(%cqmfG^KxzAGkJ376ClsuYgnJ3ss zW};2VL;DemA4axGa=?PoG0U)MdE|aoOjaU1!R*EMclVBV#BFAyeH?u`UU%ret>^=e zWOxWwu5u`)A6_BxXIm@oHf?R1cY0T_nl3#Z*Y9Wa z4tXt0tV&B+LQB0>v*v36K1~AYoNvz(0iN}Tvx9cRiQnjil%nxpEQ?ghb^$$?wq#}m z4rG?b8H(cP8M6^(aC)@cuZkN_`M;mVv_U1Fg^hPuz7Fm;TQ8lm%*eDi&TrymH@KYd zKm)Y3WYlOMf7Pvx#d`>=i9K+JyNkX$%-~z)+LrYAv{@_n7QAvGq2uuqoxo^P7)HRa zi`0l%!l>>W7W_PvRbYum6$v{2LjQnaQyxU@JrZ&4ar69EAH~#nYO~peKccX3JvPcy#%t&x3kin)LOL&&W_I|WyyZEG*Cx~fCtb_l7**INccUnketpA#fNhZn!1NPFJdagz zwp}zP5?{S~B~6e=OFF_omdkN{8jKrs2RpL*xJy(4fzivDM;uZ3P)oD;k&W3G6Bx2pmhecIM=b%aUGkh-4nxd)CC*3&jntD#46m)xlX_ zR_eJ{I{0)sAAMiq#F8exIlMz}_$0gTMG^MTb)(gGsj5*hs+9aHK;bJ|eP;ktURY1o zjvTiMwKcFOF<{#<=$6hxRvOIBpMf0eb$PrhQ(GakqG4iHn+ZObn%`ioU|vf>I0^_< zVGXWOJ+Bt$({|5iZez`XChbiM2VWuq?XykIQi~lxQD0p00xVKGS-wJ4OGTX8uq82c z(AQyNJxiLdf;qLJZ_SERiaM%Kqg~b4um)wzJW6bj(DP>X9x?Ta{h0!OdS5JN4auA% z-v)nyU~!+uVrIc^UIkQI>U78+i|)o6tYqcJ+1m+XuxnYl{-oY;{sJ?1?2#m`TUcvt zd89V%$=%7;=$!yQG-Lwmqh zNQu?Zhy&SYd|~;IiJ>_WR@>@|JmZ;sH$IEWqz!mjquYxnV0A^0{V!U(!J0RMUD~8p z-}q;^3UjsFK7DOEAKL7RE+bn-Ojs-BN@XL=U^lR8qdcFHvZpJ0wh$AfEWe9=ZL&7O z%fuvIGY+gcpG1cWmTRDqnnU6 zuR-h4dXuRAKM9|nHIfU?>IHgV7{l_r_JgAm`H%kI68^Wgg#X@X|NAh(-zP2pYfh8o zJk&sPFVBiNxTaDc+n zPp~JGg5%V;ad=m+ed>HN4oedUHiq53mV)IK+WT|I9)`5jHC(#gTQ`rr_ehC_PTXYF zqTls|r!PJ1fOAuzDGZ~>*}9}XsKD+@^5|5XI?yJ{W49qmosp|(;~<*Y&qOj;^*YqW zV*dqG^lXqh8>w%jR!G9f4pA`MY`pyls+x7{w{wW)GuotXT9<{(RghZIBW>HPF%Ny4 z|`CJ=FyQ!(_q7FYfh6{}3TusUC%H z>wkQ7idvY=*)xxk5p$2!@(fULem0D_;%r$6)`!%T z9kkr^R!Ni5MO)Tc&UKHCKn;SJX=qxa(8>P_BY$IB8BT>rzLJ`}yrTUvDL|0s| z5Iz&#S2y`2BXF#wumKxAqTe1|v!G|Jw6dbR!oFZ83Yqgj z?V*)*)M0rPGB<+5ee+53*#{_bs|FA%tn+b|63?y>(&io3%ujP32h5eLSgV=^{bI#i zYfMgX=)5~(K})c^QH%{#tTisKwpB0sQ%>NFNPzU0eRqosFt+|7wBHXqFamq?>!8DS zuoxDiXRN>d@G|>8kLc%YPLO3Hmg>G^N{-(H{1@9-t#3&qdvuq#(a2n>j2r1TB`y=0 z^Ad#P{^XD21>}0+Ct+!WDjfLyeVh$sivP}Ww!d?izjK$rbC>^`+~x1Hx4;GceZKgA z$>D3ScDcA|E)GxdsaNJKov&nK#l{J|^l|d!$?fw0{E=_L5*bs7J<2T?*~|xJY49m$ zYl*2@a@rl_hnnpMO2Q5PjZ@=BMMy=?9m~Dem|BaBKmslQ&V$a=CSif-c$qeVD42HB{ zVOD{=ZBE^+w4#&y|8KFK4>G0-meLFNezt<2^^+ zHf_|ZY^RtFcy3|>J+GkFdG_NR7S!PzBLb$;QO%N4VghL9mPz!2dUOB zE$rGP4WST;SQhs2k~0>_5Kl!K1MS&%6z+^XaPL{wK8Ju|eK6J^kc~29brRKi0<2Lj znx2Pm-OhbN51D01yuMZfjF4bsFyHQs=+=JeIgLpf0dj6(_qgOS z5-g9|fx4ZKGqp+Z(Ww9we0M5^>vK#(Wt}NIifuQmF)OIVd_JP9H6OQaLr0v!^UtO_ zKry4N{K4n`BuDp^8qVP3(d_Frn1|8ZtJ*3qe{m~|eH|Mv`k3j4_TVX1`wyk^5}`^c zf5$*s1C?0Zjos|X4JO4V&n>6V21Ob%UBd}wNtgnvG4AdVO&kkN&)9$<#K@v z_>UiT@xyWfvR7pS)7K^A*|}Yn;f-$XD*O0+1)Wiu;@pg^bS&an@*anz@$pnZ;gDb1 z&Tx?dX0+MERuWx7SPT&8_y;b|>Rq;C8`8a*Lfz!l%&6JqREw+vjvR+acJ|*37g~Bh zT_MQg?VR_6X@PS94iOuFO7Sypyck&#El7+fXJK8&8%A~H=YI~|>+g(#}v@SUN83&YcC*>!EFD}Gw z0g~1A1U25M9m#$T9+H7cd_D&FT(v!2#teShZG;>lyAq<+zW|C|uKNgY_eFi13%9?# z^*BALxHqh^z;BWPfLO;HR8WwRiZN$Wy>y_A9$*dAsbcv6^ChW`h0+ObgK_ToIwFFb}= zP7Ls?-lx!xpJaL0pQB@6JR^CdE(*uas-CjQ5+lmvCLIN+<^v%&665a6#7II(^8&(d6Ie>Y1^wft(a%z2L(bq(@8CEgFA zjO`Dl9|}tO`#a>1|1CKjJxdWiXK0H`rDOP7D8~UE)gm@XsNVtkg4havEx8GKGtJ3L zfy2N0es4jEh7?=j$gGfWh8hotPby}O*R*1O`EUKb#GBhh(Gj&tGcJD*j=@b-8f zzDbKzB=|3`eguvlascriB+X&)1Q8JEeh8`{7Wjb&T*1=nr$1vQNh#uN z&{DbnuvgZmn?Xw``J`ql%MR!FLiL9=GG}XL`l0^_)Mh~Z&Dp5b+^WB){XZ<^(FNRo z+%c{-SPp!qN%;{%iBh8wPSU&{V0l554Wb@t20?4eT6TIUlMAiV z$>ic2aJq8R91pF6Q1vN6DY1eqcq#`on)|=`CbGYa*)t&4hXbdvIeF3v1C-}``+LV~ zWLUx0|wbNCEJ`Ze-y0>yIz_~MZkHxgPf~vu;8Ru$(0#|w57V5^p~^J-r|SI zw?=OC%rgK(cu%R`c>#Mhn%-aTuN568GLEp+GUsV51FY_Zvgt`VJS;lk7O21rPCT=z zS`k&FWuD`h3Jx#rq+I*dqRW(~r{&M;5(@ZurfFcin<}NR985ESnER)&+s@&sXM?Eq z9zn$@@0RC@uRK)IJm>_J737QGyND~kZ~U_wr261e&! zHAf06QVZV1y-Luwz?rK7p?@TlzArJ{3Iw2Z zhx64vJ;l4AH&IQI0(sj1dW$&g7$0fh7Yiod{AUgVsQr+6F`=a-Li#><)jp61bR-X%$y2S;n8wo)(=2537=a|HLs_*EuRrw zxd0F|jb_^bh91=n+a6m@DXjS7)Q3pDdDzp$!9F*0zlP@s7sAoB zdxYv6Ny^O)WiA6K=v+Sq(EnvOF^?tqUl?3ywLAl9?gbnj3;Zl=%nopUh6gcc(0lE2 zy7ytB_A{>Uur_R5Xi#Gbh&;J_8qIz7p4;{$-~h{?=`qM++huy67HB-fX!9?8ycf@_ zK?pHW5)8ukCy%#m5q?aZH&U& zA;<1J&2P++63nJf&@yA%&*hI@&|8^TP_lxrkNoBft0sY}XVLo_AfA~D>Cy+)&wh~& z7-xp*Y`QCnI)sqZocf;&+5*mRMc!=H}}Ug0e_}jdp3Y0+^&P=7?R*D1nuTIGR?JKR~Dkd zem`cOjCppI+p(;>;w0>yK}|Sd(a3oEzM&lJ-R2yxI>yv{Qq7J^k$5_ZXV2D`5! zk{Nf^a1^sRrn$oa#<;~RNdqwaMQ<8i&h!}#e}gPI+iwB~y^Rb%8=Y6|8m}t4z!2s$ z+L^#NhiauU_I`EqRu>i2n#&pJ%=m{S)q=Xj3+*K!#x#TEc#3rOeuiCdc?e{K*^VF| z>il{I{RY*DQ_Y^yQ$fcGNA+KKXy#Lr~ryPi3Ukw$#+}HD`cs? z6B5++N~X8NK;ij<3EBF8x=WoPyT<2SqMi4&&|KvJh0@Tp8T8gXV30j5b1*M1CQ+kk zezJ96iW7)i(|%`zop$0S@3y2EnjVt77I+XE4ZU`@>HASW1ILj)f0_lZnwVbez&eIVwrSt1XforU#p5wihk|*1IQNwHq4m7NKBpR3h~rm;85tl<#XY34?<_Jdps0t zpIKWJ1F#5ClxttBttrKSL?1{(K~DuxZtB$e{^p{JMj8qsP$FljY(KPg?kt&>xbiqc z^AFQalnZ3Ah@Ku4O(`rYq(%V_1bb#&-bWGnv_dO|dQz&Z12~Kh8c4vghELe3dC0IU zKyDx33O|@~53bGyd0UA(ShHXH=}Z6!l@+?+B)SX8Qi3bZb*(*0ME4Xwv#oZ z=a>)=_n@qhK73jby(motB>WiwdN>Hy)~B!A+8=N7hk{OL1y~Mzq8ju)Ow$8TNEBy* zA&hjPR1XC3sr$SEL_e@5-!`--l&F_P&p;L!n9zDaH{jlHwti5h;Uw-eno&gDn6DD~ ze`$y#Z3nz>t1jzEvd~XhNN<6dnmI8bY%E>BlT2OUE$nBDn=l}Dbh2hp4&=>pd4m@M zPb66TTz*#-ur||^7+uElMz`LJ&-wTUb%Ro5HWaTLYYEy&l4t1j0pkXmc#Ni}ngyO* zC77b&4!u86`=v{P9U6ZQeeMsx&B^30fpofShE4&nX5S13Zt6UzMv>8(#f`o?gGL~| z9(2yeRHlW!5wLBe-50(Wsfx^?IP%Fu#PRWC6GTbT(>R{Wf&yI#;LV_r$&o$ljpF5C zlcqehde2B@TSSh2M*?^Or8LGGXyZIlUT;DLutN|I-cpJYxEVO!3ix$$G+5fO*6AkEC)GCkb~AeI7aL;Asle%~Hm#hWxJ zKRF5F-QLbWmOFqY`#qPB=wgB&OEYd9?;jmzt%P*y403=iN{?58Hy`kd?1@y!Qdfap zli8+kp|m;EC>+b%>WgPKoa2w5f5^Duc<>muDhjAxAJjpEd8Y_{Z?Vy#)dDG9y zvs%)jV7j-V+bKW=Uvx#;x>~4ncV{ukiRTTeU&;>za-LVK9yOet-Ha~IOJmq6qwOLk z`X^y_lft;c$W3Ir3j-c9+AY-))N(R-BlMRdMtGzdvav31bo7W$TZ5K=2PEdtv<Zz+k0Yz^c8{yAsWh_R=`?Dc;v}}f`AW)p+gz8?v8pa!KUmdBVLky%PfOlU4&OpUy z3c7~C;)DtAF^R#ImH;`;V8eRqo(;H1wb9WxP7|d9Wx;F%CJ#D}?f_PH@XrO;AJ`mA zA zA-+AQfnU2llZ~fPL46n;RQW`evI0B+%C?^XuB`GJPmiiBAU~L)T4j-Vro|_nrC?r( z42Z=_W3inv7Kq&5{y7ugT`C-Wug_g;F!y<-gPX)KlpNqeWvUMpz&g@(7ze@1*B7)| zrHliA+S_HUcSkB%OrJ8sCf1?)Q$is_FrP*mccGa2PgS_j3?76Mp+_26qxXd8)O%Dj z>{I8vDC-0HGfWmhj^<$Eo1_s{uF>X33yw1ieC4KL2o?OA?@WuU510f%Jp!uvy^Pe? zU`D}-;ecfP3>N{?OiYs=bvX77x;2=$6`t$DOi)(BX8IARQJWqJ2L`gc0p5wKP|^s4 zCi~pPP(kH*p=hk5;>;;*O*gHgP8tra#PuIeI~yhCzicZ~pn^6vXftn=_eZC+z^y5NY0_iKl<-(tYuzfo#(S5d3DEandfz3f$la)I}`8nyHZ-sLa_vbEROHP5RIs zvBYHoQZ*?voy+S2rWrF6(~lT8D1GKnGMO$6AK)r_p+pxo!&M}if=rKsQ;+mqHVE>; z7a%DXp|3$O+CMEHuAP|rEF6!=)&f?WLGARDf%J(4ApHn6ogt+Eyoo>=%>Ohf8a86R zWg{1T@Q|M$1`GeG>3@|_%bmN2*BG0iHB9NLX^;(pGoVv?eJo?5uIo0jOWXLHxoeGA z14TK_P}EOsgzEb0U{8%02^A9v>MNf`0MigTb)M{<2=RqXO~C;%8QNl`&PiFJ@-zEv zLg!$ae0(5C8PdQG=oBcN*kBx(1))r-|7Y33f8JqTyeo6AKR@h&1zBFn z)fvb!Z9(fy)k!~xWe~-%23ij_&rn4fBqrDl*LW{^7%>9Xc8oynb1JYOR^lb?KgxKr zO>wfJI}~cocJ1lT%I&$~!-if`a1{E~3=At%;3z;lcFFe!&QM|nNP%zyw&rG+-vEgf zgs|06fI$7)c^^uw0A1OTb|~L`qoxbR4$#AIasvNK;@vtU=Eq)&t6qfZuGS7 zNlSAZR5XT86-{@1=5xEJYMw)HEEwS^$XutUuu9n^reYwu9s+wkpe*p!%@t5qc=Vzi z46e5E^J~xo)&QI}n;!T-qzKZ$!(egAn8Kod@G&iu(r9uezrNQ5$!&}86rL8+x=ixZeF^@s-kT245Bf*hiT!6$)y z;kVt&t$<0{qEhIO4ZJ%{$S9Sc;B_?6`vCBZ(~_>=cU_mz77k95CMAKi?2Eoc@P^0% zL^UW5hd%qP+iye#wZcAdSPqcrWi%5G^@C#B&_^MZ?Ha5UqaGWDODDJ0sfdT4YBoUr zofrU-Z+C*#`wR>nhJpGB#1zP8PKJX~p>=^$kw8H^K#*J-3Q8OhLOzKHjEJ5IA*g^e z6JzUOV39o>MS>hq9o2A&7_33k-SDJKjVFuQPxSuTRa-{581f{8js2tv`V_MW`A$ zn0N8Hjc*bG5;RC5rY7)S=vi|yV+DAx5NzksnWUR`cAW*aSHVSDeSr#s!7VKY{&PuF z0p>+oE&yi|*g%7dSxPpj-@W?qlqv1*4#DpYyh{3DAq(ihirvBsgAhC51AVb%&u7X$ zx-L*xf2qE^zgq45xh#Jl;kU?n-JGsO0k5nI1=KgN|&Ty#K# z(uyW#Z4(ZzLEm5JF$Wgf4~h~~>{w9_+wKH=7{);X1hKjMQ%114OJ4%6xUf)E-$Cd{ z(T~u9l4`&X#gx!evFJ9~#b{N*kh{2p8_mZ_WXvwIz0d@n^0xjUd~}0y5{u6!0Xv2X zYQ3``tE?{~%Wr>wkOImN>EONj!4Z9xlrj$w725e$|dRrLVpHp~4$sqkFNIhy^?N0l@=6f&eE=vXOQWSkU_U9yEd zwcJWAB2-nZ$wHn0FJ-uQR2Ia|RWVJXVa)iVDvl%?yfZA&vhUuIiUet z90txg3%upQ_KXg<$HLYlULrW`3QR@$0Lm0@oxB+GABB~rb8j_ZX_*6#b@#Am2ZJO} zlx%|<pK%}9t&wUt+!zz z7_C1Lc)R;~K1Y?4n*67+6x5G<1Y;zNjWVV?sT-6qt5%}^xm~iCFmuz7m#cYJ zNp;|C^)99++^6oD;N{agaPmArKGQtPJLP9r?Yz4BG2`(Mvi;t)%#|qhzaVk8N?0(B zTxA(}V3iOrP{aT8K&G8ec;xmhofvjc{#K|_<=uP_wxt4OVQO)>*nX>0`K~0(MG_7- z$S4nYFD>G-9Fw$Zu>|~KhSKY$5>u$c!0Bu0&c?-1M4H!=b;rUAHKPVgcv{6KUc=Qe&_|p}e%%i{35XDt-svA4{G>*V^FL#_GSfPV~42625#gHIiwX=G_|>(+oH;XeDU z6~8~Rr1e8ob*Y1+msN@yxLEmeCu|C$=DXp^-F)8 z9q@SX$FIzn|FQ6h^ZvcT-_0_*^|2(t;*Z&PR;<~~(ERC+#(?;zr)k0c6@F5?rWQg* zo{6wkf~8;|KC>H~c<$=Q7`kIGSEvc=dGeH=q(Hi2bOM>&O7TvJs1aM=bRWYg=-+XT z@ItsoQ#Vgu)^j45P6?DI)@1wky7sPL&?}1l1(|i`(QzC_)YT&%8ykzZ3ihCgL_P4} zah0F3GDicPRPp;r7I6JANly*@*u)aAU${xPw8r{Xg+h6c_(C_uc)SYBOD}hv=o*WL z7fA-K=>gJ95_iy}MBa>Ex(s%Wtkr8ajF8C&y1KgX_gT*(x$2!j84r=A8i#m?`)QC?&fvZt(jZcRuYzTCDZJXP zYQz)`3JWug_;x|)UiuR5&jP&MK_M)qi098{v@{9vO}IzY35~cwS-3V%v|y4Y_mh;v z$6-skKXa3^83scfL{@(^I!RQnz=lvn6JyrJIBD-ke+QX#BHXjx5hN6_#u99DHf&w5 zS+DUa$_0H;pa_rvt~z?OTqeX=o|y1WV2^5f%}vGbiLtC-3nvM@B-?*#)}i;&?}Of8 zd(LMw$kMW&*c`+B5^~B1;x_WqgwRIZW*0mVL{=}>A_u)s{9P-cyIMSx*7cv&>lR| zT^3QUSSQTcpvZgHq-du)28dNo$@t4%7c(@)W>qT<4WEs@$+DZU{%=Moog)D za=wv7=18zsq8cbyhFO=*R2!h9I6$i6Wv(T6ffm%E9xoW{v|-<3zQoik`tA-I2qttkp5HG3bKaUGuqQ1jX zXB%3^kFJSwgdlb3Gw5Yd7{P+TA`Gvm2Pz7{KFq`nKrlW^y&sRjVx*w*4BKh^8B3tP z+I@i{8l3=3H(2P7&)f{~fgI!rLfQi%tYr0XS_<6z zU*GH%@L0eIO-OW!L_Q z9v5)?3o(Gcv6SE*;GZSsP(c=U_cILumkP&;M-4hhEjyvR{%>X)d_M5)YF@?Vp^)m@ z=g(xeOgg^f70B(2Z}!XZ<(y}q`PpL~XcKv7hbloG+K_-{UItuJ;@-~w@phUvK{ddp z8U4FSa6lj4kt`pxjY1xy<7Bp=U8t{gOr@HkHoqKX4o9X#&NA?a?G%9tAee^}iscWv zf*}TE#Th+{AT_lbiRJ3Q=s?F9S4;V0;aCc0XdFEsG;e&3{i$VRgv|MSkX$6a40h-| zdy4Xnm`k9`6ixJvIe``@ffu_|^s*VP`2zN9@A}IAxem)F zLqLtwU-Sh%U;*lq$TL~m0=$_v?0d`yF6L5f*kp78viLdZ>uJ|AGv;8%}zehbUJS@kX|JeU<&!Q-@MoznxN@WCr?X$`I1WD zuL&k(t{|MS8rp?}GH$@>Qebcxkj_X@F(?7vnl57XmuCT0o%9|?fQ#P(Ct(C~4w+`9 ziG*5Ox{9Hgn1J$c1DwRZpA*GsSpJ2KUV@mx#g_;32Jjq3_ry`q_E*Kg#F-HFIpr;X z1+r@lp_ zHyB|u{HAh1%3CaL)nDj7^^F!M3b(Wx&k4CGh0YWfUzep8^a=3_x@Cf*|2r}8Wf})U z_%g&zFs2auK_d(VkxK=8wf`+Q$jJ4 zLx1_X86EgnPT{UtTFz0+p$?SxWP#{or!GOE|H2gU+Z}`XGN)_|zmcGb?nbLascR?q zUkv)2s8SI+;Q~jB>0{C1Jq2L;e>w2?6me&bBA2?6p%|!il=H(K<=j{#6iELCCBe8c ztz{HhPt8rr=)jo98_fS;@4LgAJiq><)`?2hDk@7{R6(esh8-36fI2D4Z~;!j6v9Zb zR#A~9D#b9Y3l$Ls6_61GA*_H3h^#~j5LpQ!KoYWk=LuSE6$SiW@Adn>Z~6yUF+6$h zamMF-&biM`=OM)=4SmSWx^4x+UN)L0DQ7E0dG}mXAP2#BYrP2~IEov;B9-A8J?c{= zd>Ro}C0T34_%msUrpWoe$K>`WM2$H-_=_A88kEU#^H`;cIK+NTLA^)l#XSNFgY3w8 z7f!&!2D~{xRd;p=;%+_y7QkL+L2ajcKQEDf?plKtm(m=?WHOEa(?{J~Di|lBLP1ET z?{Slv+N;Q-x$HcE`ADp_vwpbsrJ|TQb_k9bDNdgU3PgIAsTL9jcI#s-2|yyvn`QL= z^UTjahjeQ&F}Ro;FacZ4xT|UF^^4B z6}QpV2^~EOG;tjdnBdH#Aa;E{0&uI2C@(LM71r@8L%Av=BjkgkVPZ9>z*2x^S8TXB z%fHv29|{4CuqgV{pHkfDcUggKCT{Jz%h@EwkL~7&Zh_%joznE#Qp4s~?~TEww+ML% zOwHWX_lOX*+v;=^$E1aNisau28&nSi&i>_O-@}n2axT$cVzQM*>XE}r(M|GbPzNM3 zF4(kYiXdP`)QYIGnE(m)8!uRhkP(t4x9uf=0&~%jNJm)lq}_dw9t0%^@OL#ih}6so z5>OH)`;m$djBnp#GU_HK1*#It$?)guQh@C66ih0(jR5D;5C|@VV}jKm8NN z)SjD2NI;Ch*QNl=8b-q@IRXgpu#YBn@mgpGNO86!+%-W(D2ofkK+Rr_yS)lfTfX70Tf27-ZOSSQ}nG|H>!E0h8xYZ%M?-d^}ps=yO8qw3G!I(EiRq{qi zVQ=CZPVr3)QHRhBtqgb*ar9gK0d$?`KYVuN0qxchKR&F$0R#)$OTEKIkNxn+2nVyE zW2p9q#?pyMRNJ9oCdn;}PR}PH1!Q9pnl4f5$JQHurnCED79DhLiPu&$YR`*y@-#5z z-s2FWt=IX_QTc2jiC~%dm1|fe4u#y*NO7kM+7-y!V0TA-bzm{q z_DC8$iS@x}hp7L{>>^gxmo#1~%m1>U2)uq1-@B>*d{wbExQ=MyNgE{sXu|b$KwP%U@T@RerX`PTG1@!eH0z4J|3#r@^YnO_OB6vfQ=;Y z;3y&CX66}jcWN(AjBg*O`Gz|rv88Yom}CJb(vpLG4_I0E3(__80YXrT~QRRQK+kq7Pm z;M0yPzO=D8^CkPKYE+EI3Of+@5&%1#``$f{t_cg67%C6QsZaQ{P@8ge_*b`T6;|XXt`aUZx6y^#O4VMPDZ+e6FvEZ5DR~z%wjWa2Ovq z8u(;kt^7Hfie%X6e*iL&;lgNR!|}SpNxG=}SKeENol`(3q^QNT3M=*}KV{z~2zaV% z|H0mW!tk{0dGp&esTy$$fFtUAbjIH7x*WRdan4dZ&8vf=;{kpHM zE@G?g5k-1$(7y;n#sWqDW+SXP!j42jnDj-EiMWV}GqA=V{Kyv}mUM_ngaPD|`_gX3 zFT%o8z@#|N1?50!oh?EpIv3*OPS}L6|Fu+=A{ILy0jA|zjq7I)xCByW(HT6k0oK@? z5k@W_1-Fq97h0r2ju!7(p!dS*ikd`VaR88j;`_uhUzV|-SL@817xoxZ52N{&~ zeQCIcIC_P3Mpv^s62 zd7oH|K3wmy*I4|LVl;t@#q)WIW(Z*S{KQQ5HAO!_;qmT7KUSItn}bEPJIO{==ZDbB zMhh^Gy)2(v$-W`Finy1J!^n+5GJOF)gRR2&q*K!bJtTv!SIIO(vD`MuosB?sKEqsj z`5p&cSPRVA7oaZC%I-j))1wpl1$a(=tc`?Ig4(_LJvs6Sm^Q$cQs1x=qUA3@OeE3e z6@jH1;X5uuet}GK25S3Kj_Qx4ic-)tRrfjtxVb)_^aL)P&Y`Q@R6?4n2Fn|EZphuh z55f!vln5UA3)l}Fk`T$TA|b#NKwtE78&46r4wadO2nY~>2H<2n-7&x5y?T>+eONgd zJf7Fy-Y%k(2@hy#J(bz&u==~Ve^9!kC(|_Km0HnT-t^%`SultkHr*az1u&Pf2!@fW zv_?IQyO43Qwr~FnuN5Ot3Khk0rfC3=^4TYPI9R+G95O>;*l)^U%cVGTml~L53*-3K zKAk3gJtr5U#Eb-R=gskHIO$FSAdzm=jy&)Nmr68J`}FBKNMMT5$1h@{NghHnvgbol zg@2o|Z_k-8B1bkdY{VX~vBrchx}P7!wnH)jg7@xO^%z9JJ8-srI!t3+%|WWLgQU~N z0J6YcmB0@u$q-IM-Sz(yDpUOXVdl8UjYF6558_vtOC*DH}vNzo|6ltMLe(&IdnpB zGZ`^7z41qg`!@sy(50ccA}d-n(vj-br9ed-npE!~m!9|yi3dfYlSv&pKw14ZSql5= zZ}4id5fJ{gSbSY&D{&-#Jv-jp6T*ZYIq##Y)-$c@z71f}uMh9&L#~fVN@#|XHHyPB z^+7IYnf1mB`TI!gAjJ4sQF092C68xrYyegy7O^5Q8{jC{&khpJh2Y|2QADK&BwXn> z9(3TUG!adQA{K;*3^DAb|38wNZ$*v%W*MEjr4ZWi%#1zRVzeqj1a1O5!WVKwA0pp5 z=gHEn$VYZjGmVz!Vf0)$%vcw%^X-?U@4M*bckKmL(8-HK-T&pE4)>pY>q#y#n#8$5zR?JdPtsbefZ3Zq|d%=O|WU4MQ zpFW}h1jcwWBIN^Bhh`!X*L%5H5%uhgsPZYWCte>1jAVemfN%aW0)(FZB7-^=eD?ta zcDxs?K6pw1(T*7y@lzg=F0sO?sCQJphepADFjS9d-W^Mz7Gn(Rp&hAIFU0E`bIQka z_(4=3MgUn7%(L<2^2VW_hl@-ZE1BCr5W?I-0 zYL0=z8+IYE)?JA)gy|zuJqfg?h`bN>63Iw^A!~vp9GbG*88q*nW_20{zNi6RiACLg z+TR6KO~L8ng*`!OI6CAKqK(8CA)`FRH@BZ_nBjp`(a`DY+9Xl4x^|!{MohjHbx9d@ zpkHbQQrBQ0pH1*>W}9W>crRL1#hp&CaXsi*)Rl#T$l^0KADG!M+oIlV&MAU@a~R|* zvfc5#7yhht@r#k<_+iWJyA2{IrF_Fofg!a?(_=1XWkekn*B`7H;#=IGsROvNmgu3) zOepax6%pzfL;Pga&cuFf7?S3yivyVlJcQ$O$e8ZN_H^w)n;(2%_2<11JX_vDoX zV_>hfsm8B}Xk$bCaMa~~y?rl?5lGY+Jvz$OtMh_Bz#>tTt?iZpbjiI3p;h5XmJR|3VBpU;%&>OeX)+ zO{nsI!%J12N;2xLIR5sV)SWZO$Sp$wi6q$MKPg zgE*&!b`!Ba+59fHyYb0S6ZwBnXXW_c_6!;IQKsBq;yKz%++2>>{(bSlKN3_@F`1;d z`{eK$-xgq`w9Z?}elEQ1x8op>Ad4EBsBg<-Vl$r8wlA9RH<$-sJtz^HfPmi31bH*9 z_nkb&_uKMT31#sTo(O=^0Ed~54Aa|NhzGnkfcIIr!37Wg^Dr2^0U#Q%L;v2mzUYf7dC1Zd?MOT#5DcSF)&&JbF^fdm!rtq?69SsxlB$xZysn3F5<5 zftnp3C?&+=0?`)!uaW~WQ@(Rvvs@vRo4IBR&i*}@hj^ia5#zgwKooqe-GNvwb7JeX zEPQFTRINef&T23s7dzgk6#=CBc*;XM;F2HVOFz|Rq%Q8up%M&12eN5Np9n5+x<0--G%_P=u||B2PUFiwfqbBfbK zXL-Y>_K~{%@{1CLfNc77_#5uqj{;)p6Z;B0;#UbQ5Z6>tT_QsLr>}BY z+p#6$A?iNuda^U9@55z%(88ecyA=SSi*D)>E5Jyet_iNcp?h%1ZqhoZ2CxzbkM~89 zwxTn&04fVMx%Aqi6t(;fPA`XMB4`qD%FCgBQR1($zE)DJ(RjoaqFzlhE72NsxmJI3^paN zj|2ipR%;eMCXu$-o`bmK5XJLpKdCQ30YJPL0Ul&Qa37pK;HB3mMW*(ogT;+q083_~ zXXECX?u3HSEtY!Uk^wo0xbV=$w#8}Y$d%u_kpQ}M0dy(U-7^GJyY@KHj}}Y6B7h%W z&WUass{|%Jo<1~KcZenK&2GS|u&;?n`XNaSOzAXT@E+D=5L~-g{SsVcTMZ@Q#hFe( zEbD8*8YlLDjrk?6BLaa{u5bd$4R`CeNrL~#w-xTh;q08VkFjyJWfBNQSW96 zdunIZ?>+rwSu^oEg$z-v`szwh8l9Gcuj&ip;`E3g*Y)@bk=><|#7R(G53cS$O*rje z^@&pTMBHrz<>NcSBEmb7qK0pZ0b;{I>V@|DP(i*UuSFMe)JOob9zhwGC|}c@ko?pqdoX2sl&OtkJWa@3-CL-$Is1 zyWZ>UUM$3NM5m7L16$Qcg6N;p`N@M)b@-+i`KS}%Af?`J6Dj0 z1p}7~t!MZc9mmD)4G31lhhC*4;3ak+B=a9+U^%ptX96TAKG?8 z?g0(pD8)(-fB~4^0qqgQ8Y2Q5??nK38kk_bSpo6Qur)5`#eksx-d1mF34ZT|$UFoJ ze?t!}^DZi&IzH&$NlZv3_QK?j;%SJp`oBF`#9Zy|!Qb z`v1csf1q~OlLsKv2=ba2PlizDnx-RY1suW#Rs7c}wmqbO$*`C?vKSjAIhh>V;W-w< zk%FP>iv$5Hqq4rlfqp8-gk^Jv;|hAf`xGF;_%&L?QO(>Mm3R1q+pC|~f`nRfP+{{m zYU0y@!lner+(n`tO~Aolr56;O8;1L)eZaxXqZ06-B?-$0q$;wB$#x8m1HC`_`A?mZ ziK1wPOvlvBJ{;HuqWDv@2)X$Gf32dy2hf8reJKG+*&NsS185H^E*uUNTi+G3_i1ld zO!*BpGtYpCNdOd?IXNqHVav<@c}6)+)P(v-imC+=h;PR1dGluj2j4yETI;F({h(`; zVNEVB2xcJ1N9Q=6loF8)&3-Y&3R#&C5{7qtSX+ogk?`8$%}P5`NZwY!;jjXqp1jS* zpOAyIW|c#6A6E4}{rGjP@r&c#BgYLttS3UvA{(d+fPZ$!PraGYPmcj3OF30{$Kz#D zn1q6E;`{i2hE{Nr7{N8tQ2tv1uOmp9#;zG9sus3_O+iS3goU&UsJ~M~swC$2D?t7@ z*4|=N;<|pt@}KV7NvPhj+tM%EK?}su7CImHo^*nKHf8;b5I@1%qBuV8IC82S>S6+nW=_);w|*<#_Rr@g3Mkk6#o}0kK`EG$$LT$~`wXvhyMIys zCmM%TI&JJHmGX%SAr)AmgIE&T)WAv0cI{sbDB=PZ5xHih7NLkuSh+-Mr3WHn<|?{N zOE|O#lE%Lu*QiGlceO}9t-o4R5;F$U`v3#KOaZ(DS%*)o2Z$kRGdk}eM7i^ z+1m?c$LxUs0pYMA3qQ=BpfG=GBS|TceZo)vtHyp8M}lWp^@Lv7VLl4#1$dC;4R7E~)%~gC zf>&f7k?{?SH>)>Li2o4+g?7kKKtBAxXM>^GjldnoNFqy?)vUvbAw&tUANbUR;qW(+ zQTb#}$6!_sO8R#ttsIn)Xhz$&T*@1iPBd_i)J`0fgd})J#qF9oD20IXnL+A56yiU2 zI5Q}1urMxpwIF0r%Ksj7Ao(_=jc5Nf7{MI0;9ksulsF-+Pt8CFk0|4I`hVT%oD;2P zqi4e-(e6l<#fO&~?phP|YUbDNBm!5)L;QJ0(OOs7bk~(cmYJOAN7Tdw8n3XbVt1GH`Ha*Up79B!ZVG$ zrciA>uOVJFgVV%r88_&!3x{vXUHDvy)Lt(j$4l~|wUaoD5AXw;IP(}Sg~zucjiss-RfTm8h1$kAZWq>r#nWI&`xp&o38zPs zB%Xiri(3dMnt6}1?Sn|2joLxcKApToKa8l=6`DL&cTaOi(sX1SdgvsE{r*+ zF~}x^%aeEstKOa<+w^b<$C|s$XhwKveQB4bpT!92SkFO@99&Q5Nb#F}OJ3>Dz7Nga zW>es(pu91G4_9ea(xcDnv@U@L*Jj(&d*TMa^#MsL8Jr&4nI5>9qLggI=V0(I!b@z0 z;(r)36{J(ZLwBkaI(@@zFEQ315gw0prlfDBV;ndfvdy~=^{G0870x*Lt{};ivLra3 z+nqzlLPNX!%0Ua$s|slJuS$4(0$S&I3KN~Fx_IxUgWi%ZxTM59h}2#R-_}E=Yx^7% z*6$y*;Itfu9-HuO<(N2L7h$;&I?kBpe(F;OvJH{Po~)1@WJBk1cN54HAc*{K@IZ`A9B42Gz6H8Gc{i;AZ7au2WGw!!#cHwP z!LhR*o7;OyDI+_I8xhEDd(sf;#Cwp{KfAn%g;s^@Qu0Xx45eG5E7s;Fip}BZg96T8 zKvR)tal~N|@kLPtW=hB}iR5fSn&br=^P_uHnxu*?IbvM+>STvFn=DD>-<61wGkefA zJHHY9!HA{3mO{W%QY#T)l;MXq@_*W@b4q%GkYBL~?J9O(lb=}7al~pbVhCrMSS~shHuR8`C1C;!Nhlp1=bx4+!Up^T;aTI47TSvj|HFujV}$WR=j|^xR6cozc$3vp3Co~WDnGVE97jK3 z21B##Ca%zdZUGn)&zx-LkcD@d_01jWa99fmUSPeXeOKYjUSp?p3h|sG0-+gtTcP=N zoFm<|M`Dr^oHnbEY$Fg1(_kf`0Sj&^3xh*`DN#kXg@J@CH#lO&-fiGQ|L(O^LOqYL zG`cHZ85S9#HQPdV_qi8O%y)7^9;6|Lrym7Y`@^j8gbhhz1{_1=~@eh{0 zLMWQ>03xAYbU-+DN3wPGBuVgQv2ZUUCx7>weT_Zj19vL1kvo+zOS_n{Hn{Ihg9aOjdx$643WuGukz|=m{OcIsVdtQ$Sr1QxrHsKfB$5d}%?4nB z$#58i1~;IJK*u1J%2VvH9!4&~*S%(SPVN|p|L*F7COc%p@#eR1`BR24-pmLNq1L;$ zqV1uy?>HQ$Sbb=rBahVyZLjVHQ=n+i&aJ3l0O=LnTotCRLTHM^h~&0nJcSlvlAfOg z6h2au889(Ltr9yIdJYZjOLG}uroDnVMPV3W13S#vtA$-b6}EH2v@s&Sy9z;=8!b5) z0o{NjC`fwyXE>Gw*)=#;Hm1^h$PDZba{uganwa4r&WaBtb^DVsY`d}sjB(c(;M%@x z=mzA)I-Wg2raqmGY>oKz$^#jDNP3Y-vHoT_1O=owoJ5Tl3;?vWj#v@3f_3AS< zNYD*u;LP(H#Rnh|Mx6~XCMd?OQotW8n_L;8?3-H4XSVRnHf4@V^lB(cbwHJd+|3& z{15Kx|JYJz@H5$o(YoCx$4;hYx_i#XvL=NiZrZkA0D1Sb-etpFxeBavuXNtf90**i zgdUUE)6MoHpyWcszA@BIbTtCss>6_#vo97J#b_nIki6eShu1-if~BU6m)$fM5NOA z0a#CE+%RJQ!;kt$$!cgQ+gO31=`EMwLB-eo2jjEjbH=4Wh0J7W2$28}QZo^v_G^i| zkEUWKZh=D{)T;C%tqTpGFuY%>Bl$b*%0KL6;sAAW(kSAFCQ!rZ`~43>)>`{3Izxeu zhBS1tg$L7)BjoMZ^U)t%*~*TcHNjAnWTl1#67vc_VSW2dyM44CvMrZwxlGHyS~DWk z@_16_{yj)1GAC6J*9x?%-cF}+g#8AT?3^RNdq>mj}y}J z;ItH!B;6RWGmO*Jc7}6LP?I!oz`hK~OeHwM^nYZ!1sG{x68D^o4bTk|;%$@Sob6{zG={Apy>ha zhm6j_+IvKR4}=dc`1u(cx9n^jOzdJ##fqEUBjJi%i5s+J(7A0pm_Wd_K{VGHYWFuD z8teqbP`w{swrFqx)MlhwXplpP#)3SJKOnPQIJn4hH5+R9Dc3IzE_e&5gOVcRx&@p; z#ZQVk$U%+J!R>r2`YdS$oKk?0odF?x&$+=*Ay`8)Y(CnjDK5-Ce%9fEN0Gfd@X`~Q zzy##b!~mmjmsBt` z_W9-H$cd%*=t}0>WvHg#*%XPolAAlVS=MV_4YauA0ADn>ecn$gGLA|{4NA)$`&n8e^X!9X z=RV=IaW+R3^>8@agD_}k zKR~(-x`?JUZ9Q$+6p@z3dc9%51y)ES(VEVAE2CUZt*P#2Dj0zaOnAIrkTFF zHp`&<<$w$8kS4KcpD`iok)dH+Wv`|ISC~Pw*kGdZ$-L0w{6axsy6b?e>!4|D-lpgk z_3Ie*4L)RR{Q*}N%ESQ9c*?{;CI;Ur29+frZt{kDakS)h& z%K?47Kb(1%^CtJ0{q?=7lb#@Ij8>Ak3viNdc52;F+5_>{TQXf*6n$xGx6HJdeR?&6 zY&m4h(f1KtnRLjcLna-D{X6(2Qx=)B$dpB8JMa}o#x%V|zCKS*!wJ2uJDUnPlWI_SAL;mwhESXTqghD11ecu=%b00GI(XIHe zCgC!n_}?fLT%?F`m)gIAG3YO~xC1W7?N75NJIrx;BT3wkeB`RKkL5(rU28fHE-~#- ztE$B4Ous0}j?>6ZY0#JCLqQR=$qh(@yLS82sH@WKjY$bXVj#AH!qhDuxjB?S1{~ zS0)(k{f>`4nTOnxH~a7UGNese9DX1jsrk?T zG%lmyhpKvtWnzI;)yu>pPcBU}+-$>G!AG$W$6<9z#^P7@4~zc68c0=ELh$02l<7`t z!wFwGlshypm@&iR(C_&XKQ3Fgd0X3sYokZqni;-(Xu;FS)4{&=4q0+DX4kq}*WWQq zigQ@USbKBM%{c_jQ_k+^zJ!D_W1mj)u!~bmHm*rKJq>;uT1{ZjC7M(v-kqRs(TQFB ztn^2-?JLvHh1eGTHAh}VY{>t)vvud5ghiq&JCA$JuRFM?F2J0u;%2C$%yxaIce>9Z6?OEym zXNvKGM$OXsoJRk8hF#?P%>Pg1XX)e#*aW@nMupRz&aaZJHZdH>-nQyl;Z%!UrJWtm zNfuG(R+QYEJXfPs{?6pnF*Bp)&Z2s_wQeRO^I4O2eEm85o3pH+AwQ$;o%RT&sI^(V zIvuLngPvY@@N~@lnbx^^iyEJw_Y+e(_W$I0EbbSJaQv@v%M2&87f)m7#_kK!N9Lnn zH*VWrWbtR(Nw;9tGvbl)2TKxGN9=z=dhw%~iQ)DM>_w|DE%du&*onQQe(ibU;gk6V zohJ?rue*IZCjT_rTG^{iI-d#HD-)5$S6C?B0*#r}{Vr?0E-k5>?Jo{ENIxq>uZ6?d;&dWA|(?kP!X9($*h#j zO356b%<;*vlnhJBu#^m*%iy^z;*&*uG7>>XBFIPt880Q{rDRm6jOvuJ=Q8$O#-7U( zGO~n>EXOCy@yXK0vb3?R5|+; z!3h!?I<+bGps8x#S?t1B7kh^>$BBq~ zTpZugPMdsZy$h$S*iz8g+{U^rrWAkoayhHUmN)O+<+EI3$NJtC^G**fjqV`ypuPOc zzORm|vBiiakd%@t4X%G@8I@$bh?xG}OU`;O8_H*eSDgQKt73|mG?MOwjYlE>{ZvoN zy6&^BoOOf133^T%g8bWVC#V)2D}wtD;J+zQ5SO*lP15_@ea%j+Sh)u+{b={79yCZ^ z?YI|*oS^4pS*g~CzNzqxW!OsPoy?NLOUUv%%+Eq+1bdot6A zH!Iq8dcSjS588$`zw^jUd)2r0(tkp2>cnPH$&VWreEgc;F)yopKN8f1fcMjo{nNl# zV)y;9wr(??^Fiy!#DY>UkL1NAW?Ho_9uI#lZ%najmP>X`5FCxN&N3;=_48`NJA~;! zPD}BSudZl!oOb7Ug2D4>o9$IL)$Ipv8kc!=m)K>k@{qGpf6UBDjTQvn%JHHZCo4NK zvF)twhO58IJ8+@riMJvof&6sV{rQXJcf zPrOr<_PC|f)BWvLy|BXhWjWoAn%8$(EpY!`*l3d-+wQ1_UwbRhjra6?nx&s(Y&pNA zJ$?ACtvM=LW?^q}%6sE>v@|?&G-i@cI>xdJ^_{hWGym$E%109q+`#0>y_A%?ag1NxFr=&IWg73rEiaWRF0c}CK1=|G27b9QJCx=e@9oO zm$bhV`^&ks2U-sZoVh-?BSS0OvW%>or(JSMExR^r&*Pef+Tn?!U4_=Xr_O1#ll!Tx z_BOtO{FyE2w=o;qJtwcgnadreuxw{oj}yAs1k zO}H!~+T>sAIr?}~ebR=}&(FD&4r`v}#I(0nZmw%fJ6YMW&%+R{W=&UST4zk#u=58F zyTdBVr$Ui;jK*+ptei2PeO`gFIiq|LdEJ$VOoPYi)-c#LrMf;$5Dt}Vlg1dxCTgr>G6K3JyrDjTmpf$h9^$D#h(AF#BPNt>ega?RFXLLQTS5F5xEB8O%~5mhlzGd z>?+1ZH|@F-LHqS!TK1BWm&y-C+5b9^9g!RU*hOjgie01^Z+Me4Dt{rpyw8uy-XOL12m!l7&f)Qz_>D}|X|EGjo)WvL z()qQC+g7Qg&8iDZ4ownG#{WWyJ4}0K#cLXx7w%&pR2@XBrinF4!%qqX^LI5?w$Zx} z(n~1>np_Na+hzLo_%c#0HYvy*e|1r|vJ>ILZTiv7p30476JCzbOqDcp3n`m)E4%jw z29!AtT{b6xJC`QNz`dsWX$Uj?qKnQ>!D1Ehh9%-n%JK08m!;(HUAjKkUNULCNvE|{ zEJt%%;86yxjD3E@8bKBM1x^rmzHKKrPE1k7#ix3D9z#C~UB}G7YdZ5L;e_>`1DAYo zP9jC~7uJ5KI!?)$GtbS`2?=vT= zI-o>TREk!eR*$!y&N9hJGT1HA!txXKDVN0KdUzWgi=*p#nRPvA_gFV0YEP`=C6_VF zCTnM0Eww4uPi5jTDd$a?H`Yt+cGP>Y@3PkF)o318J<-w}U6Jl^yEM#&Sr_W>lET`v z_6c*BdGRSsd1p`DzK9DZd4huSutzS{3w#)L-RUjTE#-}Jf5|pAK&h&;jr?sB7Nwq8 ztSTNq(~37rF9~5*fO}V1hW+0x0@tl zNG?tyVp9E;J7o_xJXzgfq<3~cHc>9kw;=rp`sLUw1ReE7>#Sd{LQhFqX}eq%(@=2r z-6Jn2_9Y$H0=<|QUMDQC=&^KktgiYuk4rN$Er{HD&AKLWm#du-Dy1mbXX9^VsqqM| zBy$g)Xnlt=Bv;=cX_VKDf4SDseRkX|>qY!9j?k`Rc-Wb%hfQvah-h?#amho%EHlBK zs>T{gSOqD>o!P?laE@whx?56oA^Qm$i&uRZy9s^#&+0Sb&i=$y?mdr}foDy_&NtB_ zsP#>>D+)ys-xIxDIb_!TLWA0;o6x4zceJ9wSRcEdgBScTujo5#oOPy%~jlOW0+D|vd-fd#qDjan57#67}k#rZ1$C4FW`2@Y0ZxE z>VaL_mVK?o%RM)ATDB!7t|YcDf6c2dF7`T3KWX1$`3zR=bE58rMJBx zEsrK|uo;fxv zx3QOIJgQYJiL_pvDyCFFQIAsGR6=d6BH?!VJ;*W=HFRO40z8-(0zy+F+b&h*w6JK& zZ(WYCMX*Gd!Hdl(Cq%>d)&^CQ9?^ij&%8H>_l}vGC~QJ)m%#FF#S6!@Y93T< z=(2^K*z4yb`Q&r&tSz@^TrtaJ*F4)iM!yHGL#JvfxSH66)=bB4j@ufp&NSX#t&qUU zkY9G!X!Ioo#|fO^SGx09txX5Rov#(NcNHml!CP899xEQQNeho&QKM`&etF8S;OC@y zI}M_|qW`Qd_dc5;4zOez)0#}y7*?{HCzf@^G`fvCD;{@B|CBhOCi9R3SFBh-D4@U8 ziuR17Za1x0>FC5ZuA=@L_9SshQCfG?%;=M;Puf*!_ubNyISnX%P?Q-o)da_Q}#TxD)Ui9r5cFL)aI!mHyH+2#hSC(tyyte+1GL$b;e;(+E;Y3 z&hf7)^qU=rQ1ii zimc}E;(*pV|1|Grev;~Kt*}?a9aUQ zIjXF+?gi=TuWO!@oY32>YD7LQ+)DsuZ){R)xP1TDcFlA`;!Z2AL)ESqYtLBTdf=XN zMgG9q{AuG0Qw)j%F?J22-(EeRx#0G#DzC5=*a_ouK?u1&jkX!NsO2(av=h0O6f%iy zC)7I_$*x&`IkLqf`er3qF~8|rMhF0Uq#x-rx_RhB0h-pJHQy^h$azspa=O?Zfw{8m z48yExl0u^5&Udukw;H(E`riX9$CJiPdz?mWpaj+~KVI9=4u~*M*?%u{@5LLz2lr(+ zwU#_!);}4tp{Kn)oovz7> z9aD8-<+OO_kceV$`PS3w+zE2-b1ZjDRy*9AOl0FOGnyNjiJESUZf~%ewTo z@oG^e@yo6ePW&~6Mhfv)G7Vc=*yE^OU7V9uJS4a}i+QJfcsDi*O(Y*}VAyfzCSLKn z^~Y9oGY1ukQ1se3FCXh-?0R?C{(Z0cuk#L)i_6bzMc?5mGriL*k7#j^maR$?!8p0& zK=1d!#8;B^Y=%%_VeF3-r{vHkubh?dPBU!o$`mLcO6N}T{n-CU2(kV&lJg~<3(ahEOA+J&%QVM^mOYd6YVNi8HKZ# z-#TXKKI#yLZnIl5xBF*%KML29vcr%@x~+Yf5c-#EMF4SHB4HNe+8uhak}KIO)Pgqu z;S_DV9oG>N-N2&diBis?qf@F2yb6y5jdyjz&hN6FC--ylCUN$71t+=jY9b;Jf6BSE zY&g}}TMT-Xf@0Q_1;jMR@;50F)V&QxDN9Pbyz0sKinFd&9?S`i$`qH!=8b-< zlkSsTNebbfcDzV4bj$U+G^C-rn}wzAG>ojEyl4W!Jqi1#g5wxHaycCrt8&Tp9Hdi>&bZ)(q52UbhUmUnl1B+fRgD%WOLa4Is$r#3FjswjK_<2w-%KlS20 z=b*;W2BsJD$hC0l>nneiKMPh~=wt6*SoCnK_d}85!R*RJMn_jSR!Dqi89(80u1j=r z{h>?COgmiWP&Q&#WgGc;$z9;YG*&fYk&y;Ipm3B9UL$bS7`KZAt)+n;%(fXB(SLZQ zJsB_G=nDpar?aHdA~&Wo>Y_nJVakQAL-sFYCft{=ypzA=i9g?W@w|rfLsr?%@XuGs zBUde8WoOrJtiS8Vy&A46LMI!bHTUg4xMX2{?#`yogd|Gw!xF+>k>WH%d&2O21j%Yo zC6}XqXnMZhEoD=Np7}AeS|)9AwK(x5p6L*=ajALA)k9JIbKr4YbV$x5WwXaQJOiwA zdgr-gM&L;gJrxnJ9i;J2nENODpLjuf&_bFPnku2Bub_n{mF%y2XL>KH$qMbd;g(Mo z)$-2uU9)HwMJYa;oaLKs(@Y*-p4w3ym3E26G|)*8<%M)(>%~^o2yFuuQ!R}3TJUc3Q#I*}{Fa7qU%=>@hZCL8&E+;L(y}W2)ix#NSeegu z^U%+C92swCtoJIVFlI_er6_LhsdF0Z7!<2l3oFpi=-?ZcIMNy=#Z)lTxYw2?Zw};^ z7L&pc{?!(hB6_LW+H-q{+?yal?p@~b)ELlH3ZA*$&A>XA-*}?zK-O+`0{6%JWLJ?r1wO zD(4?FqcLfkt>PT!ti|VbofkA42EBSGQgpXG`dGWl)F7&QT50x4=k>ZGMa}2n<>MXD z#^<{OHkwT798z~?_)m%Kk0dn^mdOd_ix|r5cq(Q_-8l8R~)cEk}++3I{SKVl+(QCs0=W0 zA-0dvSx3pC)sxBgH`k7Ih~k$4bbO#&k#r*cSXy`6sQR7UX#oom2)jan&hSaRV&T*} z>q*L!S!*y;F1xWsij_m?)eR+)orkZ=-MZdw@vE`@v`1#U1eU+(9eea#pitPgvio=g zAA%A5tT+velso(GC7m|P5(g~aT4L{+vF(mBYmU+6hMY%d?5l_9c8dd2$GT;h*SS6m z-Kx-HWVZftc;i+9y5lOPt8I6@vYEq(%QdCN>6oIXVT%#?VB3wQUaHC9r&?I$Fr3$W z@sBvEo}8q4kHB$MAZ;sg4yRo;HCv};DGc^sdg&PJs%@FEa{VL!-}xmDzcn)aFi-U8 z{4KeZK&LIH!8G?Rwi6ds6Gcik61(MX#6XYO(oJP?xX=@giRsG12VGd>d*>HBGt0bb zvv;zyhHn%2u!3sVx!g=jspvstUoMPnseWp05S4a}?Yg#cO)k?y36ZpLk>YJj?W8je z8&}j^;8pv0xbtUOgn5jPNJ+oVWp#a%TN4a~s>1bK`9nWUV+9 zJzo5%+I_0(4f%`+W_4)~gL(ALy1>#%t;T2fVaUaiJFN2i_yh(x&F` z``P;Ul%h+HVs4AkP?q!r5TW0iFylL_~A(@H)Lu|D7akO!HC5ay#xaq(f z!@O6OzwKT65O&_mKqCMA2w4zp-JZkryp0~MA3LcA;_9FX%i8;Y230Y~B<)<#I3hVU z2+hl|7Bo`!#yj59yicG<+-p(5?qQhve^ zunE_$S3;4M=iTd0qjc>o*4UY~%bILIIhr*$D?`zovY=v1X)*g~7B=XN$ju)@?kv)@ z*;$mE`dJj#>TRj+$)`z8f;AqlJJQFUvSPd9v(H&hk7#}pBeE2r@jNqLiCXZo^5Pxo z9;vpC6;$54(@EGBM(av$!icgD3y(Tba=5>GFn(pdFDTZYH>9+*|)= z^&^6sy<5%i2m|DzveiF@`g0m-aS?CS+Cwem>v@!kCBxgNRFK#)H7m6gj=B6vV(oc6 zH9|AaM~7QQ?s#$8*-G>019U6a-XUu=_3hNUZdM*(7Ts@{I+uIK%CN;=XF`-)mgM@i z#I0-$(cda;?ps3HA+tydYwTXVZrV@yDa3=4)y%r;HyRIiw3kk^-caKfoTRn5VSYrF z=dmQa=F7uYF;3^NTBT$B;8pal%T+mtOM)7Ehub=yk~76cVSLIs`$FfNA=(G6@DI}D zvu-Z{HF)L0p5pRq7bQAzx}_GAInO2D=Sa{xZkzW)D=b6L_z;@BZq2SLjjm=6U*%e+ zy^GSpyB^0nEe=o??}(iw33Z&fuenTeWND{Iz#cm{qimO78(c7oPu5=y@5Vl4#K#wDwNcm0jTUlpvw~FF#>h$l5^Il+Ew4J4j?yi*B$EL|E5q(?yv_&y8a{^2Jk4>ys}t z-uef62(mXKVw9e`D{7UhNoSK%j@)AB+In;*wWMTu$2W9+MWWdeWW1z4I!H zzV5LyyL-);Xta5Uic>-cwWw40wmNv;`G{fNUTwQ{&4UuXW8R%ss9l~Va(zYCZuKZh zddvI)Y7Q#5AL_*V&T6-hvXzeryJ5AoJi&^&-KUWIq-I2U;Sts_!MYMThoyU8QKz_= zXU*z-z;owS|DCXWOgD_FQ6C@DDNS7J6eBt{&yi60^NLAiH>WrS!q- z7Wab=t=`dA*Xl}+2_|G5nKnlWNs<<&r4EXHJ4Y@+`` z@0s`aA62Hh>N-&CpOQ_w-mT7v2s+v&M89o0^&-niU<1j{v%N#CeAMDt<2RBZ!3zyj z#@Q5~d_f8)=%P@+{D{2o{%(?$AKl$!2DS0noikLD#j;O63WI`(Mc%asV||*tTU>ZE z+0L1Mt`h#Fj8T_^lHN)-1(5D?vBs)QE7C0I=Uw&GNA_b;sJXo}2utN+Jx{G~r7l^v zBQ~)mragc}z+}X0S>GYJyKE7?nVEjHy`}c#E@7P?Cci->Xl@PjZZM3D&qyyY7n^(6 zq|n~nJ9^0Ym@zZH-rcIaBu=1>xmQ8ELuMR~c_HC%OKwgfQKNQXbki%*?#5c?G4gL) z){{eH4evJCSfQhSK%sckkwkH(^U)o#|*!+h^G z$)*9jwzbjzxWN?>-#K-~+&Iy3v`wPmU{NXx!)DKV@h-w!t0q7am3a6~GK(cZN1B-$ zziakRKgMWq^xuy{iH9Q=V0Vh8;r&Sc_+QgZnwFyZt z4JxfnI!ld`YsI3}+HIt=NMHJt@*Z?cd2>CJo81)@NG90aA-T^C2vew#PjRt1NZ|XV zrpISwRO%;Kwk5tbWkyNF{3iWH_FY_e5s|6RxNPj)R#Dx!*jo;T@}4Ih#skZJdmoV~ z8?qvQbWzrl_jHc@k#;MD@Zd?idxDdK&PlloR7=H`TErd!SG2lnuEXXy!X&GRGS60V z=Q@whx6=*8N(yb-v0Uv-g!XVpF%}a;qjh1QMAuk!3F`fuQ(w%nMWNmlN(%w}yXtx- z3%rt_iML^H#RoX6K5MI<^Rg~^)Pl3}6?N@4wCy(K3gg#RJX>&wi*25??{ZLWG_{ue zTk!?9`ojYRi#D3~q8k1^Nk^>b8?z!O8gxx!s&l6h6i_IkoOJ8JM$@3ZIa`HDv|247UYc zpGTiQwQJgfs9oDyXx{$C{s)cki95IR5}ea_MiwX(*gO`X_1@M*cN1pyWOUTmSh?_O zzzB%vyx#A3j$<^sk3G?B9&e6RF8IKWVd)S3MGMDw}iA_?W_+ z{G;cL-1l6vd~sc93(@){Q-^%bP2oL)s!*4v9caN;chHwGsYRVqxMZ$y&Wdr&rP`aL zSEv-W9lfA+Hr0G1$0e-hYV%)bvHf6MmD9eVfT%SLB4t4o(`P)H|G+rPTC53K}4%u+)lcUyp zpLvn7<|=&6?fIJ-CdsQ>Xrf91t#uv&YCQIz3cY#3M?x$Um{lx1R67!?Nxa|dUd;ke zGkxZS($)v9)pOP>G~DP}p|$*Ma!XkAs*$i5Yb@LzExSNE(urM^oj+$Yp+*6jPLhj_ zs0JyIX@Ar^8&8)Cp=-poj?F#-P4BiEX4S;?3ONf@3A2-KWh9NaQ2}N)%xDSbY|UnI z0*mGvX&0)-}_`A?SnmUW}IQj_$1!-y05!<|MJWLhaTBlYt@z#E;U{p*=37& zCHIFT@BueO(~<02N_F!HG$|N8frj#~{IXd1(B(B9>*c&erJ5NleVVW0#trwpe#=U1 zWup6n5+qca&P?!gn#&2!4gU-Lwbn59lu7(=+pq9zA=TW z93|T;EKj%UZ-93KpD;iz9;eiUi+VcZiNRgRy8$Kqlz;m>L4G5>M^ueGl(ghlN%*~y zM8tNuuNKn-RyA$Et97IqY@8WUuV`G5Cr*wWmkQe0*J;Y0>H+GInh-3kq(Qh`Qwn?z zn3G4*0VP^LbSknE^oPji_rqtql{f*%X4tkXoNs`}q0fI!>fESIOVq`WI+u{vl*gC% za4roOvanEMUQD{d-tIfsuqT3M#$aiA^LHUgfKZg=(7QoIrJzFo$vVl>(FLo16NBH@qm@$^C{5yP0wA|+m3#4w_Q1~7F(0hhxZ%%})1 zV=@5yE%`m^q?(4U2$(#YetF4;EBje^^mx_trEy0ogK_EO2u!>�k&`M7!Y zh#(wO-MSBZ0xiE16H`b!<%W6$%it%Vbt{1VCbxNP_k z1`>v}0s|=(pZ)33hgBywB!w})965~#Nl}R{cT)@Q(zFi zzaMbQM;6V^kBqxI=f8Th1v-umi@OXmM_2rj)%s54O7-zgfaZXD3y4U8v$}f$2mJQO zuYc@4%2@y*-CO$&u>ubL`kJSqzXI%r*v-G*pZ=}wPl3NI>{HZlGuD;(_5K_6Ujb=D z>-}HPWc|iQKQE3J4_S{}0FO}r{RrUw4zphX40yc-T=@47|NYf}CGlUI_^&JcHz)uS z_-}apf14HVWpfsNY}VKZ4!a@`(A= z4E*P_$jAnGH$V7+ zB?hCTx4FMG0<9W_e2T1h!q}_SOCQ5tw~vsz*f3f$q~tHZ)+;!VH*&7SI2cSdzZK6S z_ZRV=&(I5bGYc6RICd5z5)VqQA-Q(oU++4-dF9CvuQXsDm$6J4N^Gbm4?b{t;r*%T zfJ>3pVA^znFyTJM%b<8yUflS(ceUfoOp7Puh7w_&%Tsm*u0$jb0<9%^A`O1#1Z0y_ z(Bt~ikK&*p;Y>0(T0+KXG?H~wd$h#qff<<_HsTD&oAZaAAuwV1r5)e)31oi$K&E5& z6i1YZ4&r)OjzHqQUtKF`u;$J0OmFO*gEy-~8@EcuT}yex&+$%;=wEe>V_<<9USE~Y z=xwI8i5``X5W3xh<&F0hM;1-TK(`I{c@*U>E&hpD%uC~^3JHm62u8Z-#WZ4QJk4OJ z%bp~^1~*?^Qw0B|aYm?I92=KDw&Ji1L)^YS}@k1}xcoL%nvNOLv z`f%F@2>{RP!g27R?+*yPh@Wav6zC5{yXUN9{K;S!M?BLP+B8UaV;((ubfzDluYzWO zjodEwQLPyyypQSH(NPoPopQw>1D4`WbqcmB(;t>fkzc&<#Q-Y_X7M{m&y0s9%J0(} zUm$2LHs+z1euSOsPGvnE+j89rO|zUo4oQjT#)rVtD+E0QG6)K8plYT=4Pq-&^on?@ z+1RS)lIBxiN8qTv^qdc&9$w|-uk9je8U!-~p*`3_nJOgRgv^=TwuKGT-})w)MKyYq zvnQsGiD}{LX>&S;3QK>CS=w9hKP_s-7+}ViVLnwwR{mWQR>AUn#yvjs+*enuDtl}A zUW}$ZYFVs3B=|LUoD3qTIne7tKd{CCD769b=OF_yED-mIsbv(65%nX8d2sPJ?2t*_ zsD8iI+l^L715>mx!kChK8GCb1@P9bGp)X{V3`F&7$)r~C0#xm!(=*bv;2oW`2zr+@CJcz@!+ zHGM<9{G5#RTl-1sb1??lLd(_&CuBu6xAe+{D*s!gkEpwFA-meI)l=F*@z-OQ$WElC z4l3%`*s!|S%YM;$EhR=9E+?KH|>}ai|0D?S!_qf!NW~Q zI=h4Pign%Zr?6p_o!PWf(ML3kAriK8;T3fwvd|Yg)yOCDvsGL<)at>|=?Zqe+PP-8 z>R?=%qj~iH84~`CHFi7eQ$=Uj3MVc=m1Af%f<3G3j0un6bk5gdE!f8B>=Y90Mv>Zt z5$FY^G0h_6z5tLOTIVdHMXrk#fZS1gImtgHEzK7;4Ayu(LIo!Y#fI-U%hW z0~fgr3lg1pQtF0Qrt2n?!-2itn49HOVSK5(Bz#B7;QaIuvUcb@?E={RO!}!{*46n0 zJfjJIJM)G6<(B@4v*I(u?3Rph(cRFy%O%Z6pT4)_>~85{Pmc!%A6(g3^;!0S5Y;$5 z`nV*%n8t`KWU}Sle77~a&_DSgND+cp7meFc;7wAC@k(PGh(G>hoki zW@FR49ng)Pg?Z^v1*+S9^dLU`RbDAJ$`3JYW&~aws(^@+r04>V4}oCv<{ywVD@LQ+ z*YrlOD)^GyKaNigG4?HbMzGj>V?{&iswtOsrsjnxMOwYsd=iQD+C}i}Y4wY-Ez(hV zcl->F>$9aYSP(I<)?X{J10L!BCWF%=;0n7$(uY-3Un}hkYUGb>8ItVcv!%4>cB$SMr@7fgfRH!-&&kO?HYmBzcVkYUO zPKR;tzQ}_Fih$iC^e@3qz`qdt8L=iFx7DeK!J^$R6^Hq4EV|HXb=u;Ffung_W48yE zw-LWOa42ra5?`%JZFye{82N3MHDNw95MFz2iLSFun7WpOKz2z#og<5n4&5 zZXL-%L7Z-eitl?}EU$)HE&L;9_NRYs&XSU(>RM!CvDyStn;%qV^+7W=xbEuL?{-gB zPiF%w1*HZ)=j-(SPuUHFfl*=WC8uS_hP7dHl2ynNfBKT}YjO^TvC)v1atAq-&>`;t zLUe(Y$Ay3N-tGpvaLVIq2ipg{$|BbSk#=3tFqgsJNU4jLeAMK0o360pZN8qi;me8Y z%sDbxE-2A{^w(cuX1QjO2ESmW*hMa7LT+wE(kS+U@<(Hv=zj6ajfS_>ba+1Y({RHvIQS`B~KwFyxp_9OX7`KK3LkZ_uip{adR3N0WC zyU2x=pV$@4vzzi+g>K$^8R~-e0hIa!~Z35;Md3(2dAPTP4m zLkz>i64$<>`l*n&=Tf&SEf%b(XAV9pUBeF?iZ({gc@#7T&Jgj#8IkX+PPxliT4lXt zez%+fT*SZ6`ooHAKqU=V1k6}O4^79PVwXPGR{s*IUs$7k^V5yr0zEz5gW3PvgbLVK znzgk?-Z!Et2}Oo-c)|7@Uln1%#Rf4haa0CmqH)MLQ-{)Bi{3j{xNMU=+1gUA?UM!7MvG{8}Tq|&3j+?KTkXxwMMVE8=LakVa+CA7; zg_8G`1Y-o}9e**TTaZ@zZeZ~WssI*=+ZZ2$7mV~sPlyYrA9fVYE2E@D&Kb)b(@f_I zq|I8Ryk^AEHi68ooj^p+*={6laYJMOWfRd68`-JM23Zlem08^vcqz`R19Hij{V?jg zCh};XofYBN@w7&<hE1A7cBi#uitT3#L}DYP()1`$0#=*S=58TQY_`DlaP& z2Gtkd8(YRnbnNARcl#_~UalyTohmPhhsR6Zyk#Yjfj$e3a*O|#=E4R>N*nHtpX{$yLyTb(1S$6n-B0J~E) zSK0l4k|bmNtW)2`81>Xx&b{+$t)af?wcYwN3mevfk#Vc)-1bqD3;MM0KS{ycL7KaS zW4oLhxM`OjiX);7T{@kj><*7s8=NlTI9xZeSM!(tI!vO{ob^wrgZcU2XTZzD5hrx} zH$Pq~#Kt<*%j`z`?ANOCD2!LRq~x$`c)z82-5mWrL{?EOMDXkmFqU=8-*RMX&E@gq zOND4w2nCuxKxI%+N12jC<9R@1*6b+~U9F$!H9&j-E=5K3_h(tO55o7zg}j1}+^jNo z*g_BY(J9OCXk(&e5?1c7T~h%p6l^mp8o9}V@kv-L_Hem_wqshk-<;y#jbd7D&d2qN znM!F8zYR4-Q9f3o#>Li!Mh>`LHQa=f{Qv|b;TMxLLrc6;?tunl$XA*#lp^jMBQ5Q~ zcJw*5Mtg(hVM|t*sS6B$WVJip$gJ%jHQYCj7FMnYc>&Q&Zq<3cQX*fBqJXC$ke zQDN}_EDYf_65mH4$bqI`85M6;@>O2k4f94!z{|od~DrPDXLpGb0ewfei_KP1kn} zo6xb^W;{G*E`ZJD>Jl^ zkM*)ANA;m{YuFC~k2xN%rPXOp@0O91HB=jApm4LFSc$kEXR(VNe zR?Ek1?V=ePV@+aL6kmHqi&}j8j?6|SbNH_~vnFxi_o}mo&ck{}iK$J|tmud$$|J~B zkfUe&CKip{cA;L+Zww+k1n8k+$}vc$Il3~^=90e=jR%rP*)c;fpq{S;^%r{gp}xOW8#?0I5GI^@0$%=5 zSU0m)Gv?LW9}f6r={~v2T6r4LrBd>OTcQu~{N>bw!RVFFy$m5la!a<`&b2S(Vyzu!nRg;+ej@l;wD*Ad+9-lb1|H};a~*bRRF^7*bbCHs;jUq_{u zfqlX)v=gf;OAa!2UX|>Q1AY)-Wy)t^9*aDvPZ!2u0z-|>!A0KRvMHsFVwJ0A4B@gW z{#ppx<})*g`tS&D5w79H9F#!}3S_|9v`khToHSSbB|dL#OGF#KMv;d4#vEJ0T0MKw zatQzGjS*YO&SFL`Z4Q+Xg#RCA?vgrZ>LxeEc<_1w=G1v z!@w`%^7DOd8pGZ|Ay)yz@gnzv*X~*wJxV4=MDQlP%i|XM`#%^X;?yT4T?~C|B(HZk zDLebeGx(tW9mITMZwewBA8-SjFI$hQM!>dkmm(|T!B;A*p?ez!Hgq;16Okgv4{TdilG zvGQt@s1b3{ywYfhuoPc(!Nn4<$=l&Yfq|A=GJs4i=?aO@I(=4K@=I$roViH{kNS8i z=cA{4-dwHK7X&>@rIe$(sr8g=kt9+xLH!|%WSj(N&E*-b<=4+&3a+anv^iBFpR}{&7yKGaxcf<@6jozK2s2r{ zxu!>8->ds}===XrHmatxy*HX(=htf`I36w9vNGdQIp!w-6UpDFoAJZUyal^ngz$z) z8U5x&>Z?vl$0gka(WLe4Y^JR~;kr_>70TA8PD#%=$8uBi6y;L;zAB|CJ#S0_SuGhn zY0VHjb@Q!H!Sw)9i@cRZ$CMEL9z+Ig=N)l42(SK=rPvZL~vN)_TBTOP&@- zQAT}>2kB{D)A@C8K8_9wu1}Irl*~u!<}cNc$hq)6iyDKVr!%|j(G;i*d*5Gc)Pd={ zjkr-?xinr}f@KjSxnZ+%Mo(L7BQwUyix5% z+{+bcO56ss#Eg&CTpP1uv|{k~lP1~M354zTO1zkMvV4W;CH znk2ngrm1Hj}s3Up`0q3>1x{_(i3%6Y*!rnSQ(8mXAl1>Jb|UI(5>|CXZ4I z+Dp#PPrYJirtdq6%?D#zVQ2qQ5Ws8FR@VLrU?YS|y-!-Jp-$MaCrTtC>SR&WQmWV9 zNyz4z$!N5Xrhl@5AdJgyriKa90#I4{s_oW3d5jIVMQb>EJEF+ok;7OuXUTclzn0?g zcF=jr2oG!<5SaEp)7SbIDEIC=7JYjO+qpfFadca!jH3A!4Sx<&YEXB}=lTlI8} zFh=y}%B*($-b6k~I`lpG(aFpl-s24W?c70VV~e}bC1s1X&0X1|Gx)Os5w2CRF}J(# zTsp>~4?HcF>7QzLBHO(QQ;rpnuf`ANNp{tuDVX||a6C>ksd&UK5eQMAk?$ktJAQ2E ztAa-^Oi`nceG%>h7d|?DnDGKA5DuO7lV8T(;@0EZ@!PSX%uTYW)w>0VAittWbY?ua zkRQ7{?-g`jra{=4?ekhH{?Uu|+=1lr|ASH=kab=27N5OjRxY)FyP0tq&!g{ruyBTv zkt=VFN9oD}TifKCW%|yk*0PGQZAqYxV+xR{@rWk*p=Y31 zA2erpXzq`n?Bf4h5dlbi)lt>k@B5607TM3;EUEkWWLpD#AHiCV_B6%NFh=GNVfc;y z6?|Oi_#L?g7FyNug7>ql3Tu?b=&nu8dFex~#qRg(fK*|ln0l=3;g4kd^2ansOVsPZ z_Zoo?>VYpEax*ut3N53QlQHk8(K`jKWh=C)=Bk7S>j~H>=UnC$+k=Xlms7CwaS zsTGgODD3lYIWBjH`Fb>M$>sNsB-o4_+YGT$wd|59|7U~Pvw7Q6#J1Bt19^Ss2k$p| z!BBU9;s=4`0IZ+WiTi0?YW$RIGiB=r=;VA|7r%LZIzW?nXPs^FNQmX3)*??VU(XQ_}%}by6tWFO6a0F zs_BEX7yaq58!rzmUmwVqYU;d_l^KLJ_cEQgsEP>c{Yrj8&~uQq(_eC<*ZmFtwvR@9 z;BlXcH~AdmGLyJvdJw-@nB)I>HJK=%<^$}2&t7&YJ8nauCfV6?d?wX%Ow$^+au0QY z2|p*7#y(gGAQn{;7fdCfX36Z%crii?CM~origGgHy;5)W8o_eQ9F+Zx+o%T}Z@OAp%Tp=J3)sbjUl05iNJ&m#Q; zF$rv5I&!9Z0KQg@+%9!=@R#xX-QLJt$_pHPhfivM6T4Q1HQsx6>EV<>tAF&Ka{GDN zb+QGC7w-KBr%Ik~>3U>bT;Oo&!u&l!$4d4!m0DGA%a=7+@mrw<){83rFY>O9)~&!? zbZ(2&q%eX;uoGl8S~-atgTWsZ}# zb=wu^$*U{Q5rb7CQ(5immjkwhm(r55;fu@txlwUN@OMdH1Sdp!ItO}X|n6zI!I@qgnBi}D+Wj@Ib%);IYdpjFr z0K`5FZ@?hB_t8QcoG4rZ_;g49(>+zi;cr%7I;>vt6SMC>HyppR`5rz>dF}*?#L>Rt zcH|#khdG2kzwUP38(SLUE=?MZa^4%4G{u<}{VacDd=AB)aou9?6#9BmE0)J@_Hq$T zJHMNAc{~nz0-+Zevp?{(msGS3Juj(66*}ifW`LEt^%WJ-_%V_D6LEt&Cun-xYwlUnagn;ixR$NvquRSX{`}Q| z`zn55*Fy9>M|Ojk)m-ILn~p zxA@UWof7#E1PgjW$#o)N_P1gxvD{ls6356bIEoj!H)clhGpn>M4(AkYV<2QDkD zoB!8eA6CTY36=zfq$4}P(p5U*2QLl`>7*+!o*GRZG0X~l2FM)HuH}Qp;=z5!bRW^3 zP`nu&*yo*3kW3>YqxF%ng0=pUZyrj=nuME_@%7F0pX3F0*=IULzWnS^@I{r&8@2)K zApgQtx3Q!9KNqVX0}qq_P(aosru;}b6gtcrxNzgGy`2mvt;?+7OZ}hm?)~*w#%2eu zpZX?^x6A1ee#$}(;I6RY3b{;^d73Fy=3w3i+mVV-HXLfJ_RU*xpj_%4u}pn`vnKN8 z3hey}uy_eQmgmw+XXPm?fMmiK%ekN`a@M&XL9di>YyI?^5U4l6<4~P%kO~66a>cpW z6&Dy^Z8$xO*VD9G0jgf)-v}%8YFgj-$ScfE`3_)?$fYPDnUq-w-jXuKG&@X=CJ818 zp)NAN_@TOLX;|WNcWz;!(!p)nx61+41H+3-x(#?9r~|N&4az8;I*XV5+js-EZ*t?h z@J%@|EB_dq0ID8?1Ul0)EvknjwS-97$Aqbz-(xmga(+TH?A zYW++6CVq2@l9k(eGR2H3u`h>N-JU=6wDjI8>ln@%e_iTMOmwVjL83PSHL*K)9^Ev4 zi|ucK!-?y;O_XBFfnWpKqvLU(zD#j!-oyfsXPWK9WZP6dY?Cq&YeK&(xWLpo$<+M6 zLib|^@cAyr=qt58Ug!@l$Z;ciecPO+TcR=p;g)hDxNEJYM!qYY<;w#p(akF|rAiRk zA@*{CD?`$&MSX^g1z-k{5y=q@J6e>X z^W*l_8aBZ@FvYtS`D$!Sh!gk^L}QTkl0jr{gm4bOP{V&f@}Y84ai7l}D5zSegy!U$ z^VV~&x5Q~OsG~c3>D2dtfUlx@d*jgQ2C#I{3?g=@CykdYE-sXOp_5rX(OrWMMj@|q za`!OK@8>#{Pdg2%7d)`-EChQGvO5|(Y>b%0^f{3pBd)>{Qv}pi913S&NDJ0f)=lqWjr-JPhjLX6unhe+dU0 zZWkYpj^ONnNGOCxPOZA@j4@+(S|0Em6`3ahq~t(q30GjzSjgxTT1NbB`Z3dpX$(K1 z;Rv*PDlp46Gc;6gErWipO&w&=i!*&)*A2P7#-<*HmvhRT9`~9W()r+Y*OH;UA*1#U`2MdV*n)XZS5_C4Co=F7t zZo7YS(y%<>CLWMCh|B-dS{%dk5Eq(VlXcj&hSsmO)No$Ubh}6PP?P#TJDz=fzaosl z2HmFOB=U}u?C}b@r&>y3!&21cmc*Hn0iW0C@I7)bPF8wex!s!(c|IHxT^zUL%Cg$a zt1cVJzA-T7SQkcrMk?N+&X{SAG9mBS=6}!!xq2GXbz<%QAf52%??#;W!|I3N&DwJp zmO?AmCSVE!^(wIx^41CMDX2_nH5rr%htlf%WjFvA-5~7xSyQHl;)@Z>8v3hsqNq-s zD$NHD!wAP?BUo^A0c$GmE9j7gL2y>@_X;OYvEHo9yRew z@l>c+X2+k6TXY)QRg!Q>o@PL$#ZSQMd8#qM{tGx=*etXR@@y)Bi$X2u!$TI8I&~tP zvyhs%mY?|RDrVHxQz+OdBiXlO>?8O}x|YoF_KLyd0bl-8&=#D@dc|=C!Tb-|0!FGq zs$n305`M1Qlac39Fi2K=T>sxiWfwluqb3k3;vWcE#VV6mVIk6WCBC#eljyOVZ(+fq z-&>Zh?%!p1SX$|5&RH2dIovb?W)0?HQMDP5UeOq4g2z;1zb-JyW zwhw3BGw>nloU-@l?Q62$@@DeZ0)xiz9WJm@fEyLv7PSmXWFhA_>x}jCX|uyJD>M`T zsvaB_7pA6gEO)f~;z)Y$-EnjvecCV|kUwlv*5n1VMKb*HhLQg*f6C&Jf*ouo=x;b= ze;aWD=S#<)C1Mg~(97qt4UU1WRq9*zoXJ0xHZyN=DgQva#<2jYtJ;!m?u>u(t;UM8 z&{yzp3U1n#XT)}$lD4^_)%zhsV4GV|?LI#+bN0PJ27@nLmUPSAhuL)YJQ#jEgQI4*!@SSukt93ej>Hv0<8AH@2?q0f5l(g*e=(i!f_DCJTzq&HW zr9lo4oFOhFE*YJfVH&Ux9?{@l=;fDw*Yxku805ap>nro%?cMPWN1j6heF-{n0L<6xTsBVWu4OUQ=n4v$%-1SHaCB&ME9e|@H>cQQAT`y)oOE^Z9(<-mxp zgf)4zI1tWOKM1p6CvSRVM+Myc+nrSN1{IAW+A6Agy~oy|QM5$yGTVwVoci zAW1LsL)T3M`bo&E^&9VlsRbS#%FlsFqw^&;Ufx|bW#WC?`o|qxhVgfbN%HA^HzLc+ z4am3A+;IMZ? zzI_rGb3=e}0kVxZaIfY*xJvw(DJ|}5=5aebo}FzMylfre&aQlJ4^0b&UVaViI|AA5 zLIpf&WMR@$+P7CMVFeZyc^RyuDAM;L$BY3HhN3rgThmm2RwE&@0QT{}s6qN3SI{X25jQH|alG&b>WT+i873*rqgO*_B5`Wt09{}@bmQnm z+yqFJn!x8?Q%*Ua$+N!@(YX~}xpV>C==MpqM-+RvmnM0vbz748-rsii?xsQXb!eui zt$H4ike#U&CvUPZg@u0xxK35vhd16yoMw2cc)EkNDcL zBooG#pb!6ay3>Lr!;6ZZR#n|&Ht3XKUMCf})OWfuu%a5OSI}*Ow#3LvE8FJl^sNcb z=(N!}vw!W}(@fL?n{ClM^{W-^rdhu*{b|MWN0|#hCg%0v-=a&|TY3*$+(37VUJs@u z;y_wo6Q$QP_|{#gjredJ6tblMJ~F{~h6FP$4`FdnlDA$(6afuJbs(-oL@neRnEU`N zy}f=>C1k-$0e_?*;5_ec0KQKWDT#V;DSnMaZ1_-#Mz$G*`y?#7Pj75=-EihVXvVuc ztSNua%FqM`_|<+6?bX4X`kBYpj(LTqOvq^~>*xPb|J-n`wd-#igBkq=n^tg8s=s6B z>dpee88i8`{;N-x37Z5rDP4ViWW$RvANTF+fxRGODra|KBN-SEd0HL;EaHCIiU~M%VZ(qz9K$|j?^7h2%U|wSJBwcf7zKQ-KY_6LVdyuDI;gpOFdbe z=?~j45c79Y=iOXaLODP{n`G1dEwHBSqmeG0t*1)PS2&zxZfH^HjgQTP8m9}fKr`>s zJo*8cr=WM>=r-%EuoB^;*p)kIkJ z{4L*Ou=P)1$M;_jVemyOo~$2es;CVWm@%H;sk-9V*XtQCza_*#g^N$#Zhv{5g>-fC;tIm8(8Bl zltRIDt>2uBe0lVRQ&-Wt;~&FuAIs12WKSbE@(-S-$#B0@s1A~Oz_*qw^g!a&bzM@6 zrvI!&-R4{Ei+PFKZc{x(hs6vm_D0d!^J$+QpmzMUqFqH@u5PGR7l=|`AhOo-c5)#e zy;mZX^aXFSLSI?y-0&D!eEeUkgX%&rb+;x(oK|K=ZCNuq`6tF7VHXYF?GF9Ant$H8 zNn1`I2nFEQ_}Y-QQ+DTg#s{?5%su?e2Ctc-cSP7Fe~-tQ@6d`mFsr0lI1q>QlJ$Q5 zU|O?z9iVs)bPKH#65rPXZrW7I*GK_1jLu(1;h)Z_KM!BjcC)?EG5C6rwN%6irR;1= z^DJKtWMU||$AX36(q*ZY1;RC*g1h4Lu#6jijco7XmZ9ZcRC3g6Y#%KMe}IgfifY>- zzU`kWH|$9syahEB5$iA*4OW>j9J>x|maHDDy%hfme2l(Yj+yF2Hz8J<^j%1EoY6QF zjx$>MR>KT#_47wPb=gavy>FV{oeZ~X(P3iu^YnV0`d)FTY+5oZ#0=9DA??Vn}`L7LvI}`xt3Xy75slZP)~P zV#-PmX1=Z!XCy)ZG@&%hl+VY3$%-tG%Wy{>a)q8bwOTru@bSQEXn<43^LuqnPjYt<*X zLL&6?ji67e0pve)MP)_GmGev|{Q2Ah(0YMb=`(|5DH|T>5?ao_NhdLdQy7eY-=A_1 zeMUgn6>!o1oio63B^n%66N;Ymi&S?yZ#c7LN~PAfrw2smUf-j#u3rNYP^D?$gyNQ>5BE!kzRs$73_(75WKUSM^PrDJs9k#3*aa?(HMSk7W*sam) z5L?N8S%vRXhL>EPr!7zSS{EA2HG9KGYq1{pACh-;`(sAi<(b6jh5CUaeX^_W4(y$- z^ly*uH*k%{Wwgnxl5~56DLBiu#)eTc_{tohYPIJcDuM6?d4h&e=A~}0imB88^j#V^ zU(q#JHGCbzhk8|f z>?8r>EE?&c^Hm!BL(zuLvRtFN=aKEh>Xy8k<`t#uhVx@Y2Zyw-6;yDY3>smvQWV$G z>i?iI>IY7QB#kjq=9Eg$4b8t+3jX1BB3t%OgQ)F8OLB)WF0wllJ8BAyw^yy;lqD5D ze|5L}*6wg!FrbhgrCv1qe$PdGl~nhh;&cbJs%-&Tew5PgQ$31vAZ|#?(o%I+*uk@Gy?e&SqxcZ5)0=OZ`Qc%#qM3)I$wud>|Hj>w2e9OPm)&2hVE`D zUGPMB_qF@07Qc762CzU2n1Jv5kWnJkMgMZ0Xvh$@FmMuzO%gCv;$E=F`NK8BJ9$Xn zlG3B!vw<|(Tl`%Jj(uF`UY_aN#gTQ}Of)`|%K8PgTlo<>g5x9i)gmJ5yscwR_ ztA_JUTyt@1#0n?YA|L%WGOrM8L^5$Qg?(obT1Pmf%#k@#yWWA&9ZKXYD*EB^L(dv&iuZgnbiR>X8ACi3 z0oLCsU>Ccd_B^o4acJFHo8h@f_l`*M`#q|o7aJzTg?wD(p06~^2s`O_kFWxxtwl^a zg|rzSdhSHZ!9n~QFOTAtBegH~#Q*pz=YgR3c-$R!ioj(R zUQ~D1L|60hfXxC^JwPmOFRxz#v5Yz@YbzXudLg z4{UTnPBB75auY{vtGb^Np^d}Z>SXxb)m}TPrCL}Yr*E+mXF2f4KUu=y)9S`R&;vt+{(hw+mp%CmewnqAEY4;P=^pf3s`s!8r9zeO@=Qz}bYl z-t3!2#WX#R4?hXH(g{6ggLm(&=|==q0B<7Eu`5=#!w4eswLKzqaWEY-r@r`?z8Ig^ z$KT(TCKkJHa~I6#=X^dbU+2I@chQ^w9;x9DMa@}lK{)Hp2%**|CFaO{5=5g4ah;s; z{+-qSF1D-=fzf$qEKR&Y@bwRq85#dHUw&*wWaQ3-f74U$TqjF#hJ9CMzFzME4f2+X z>`%uyFBgrFa^kv(XTsWhCJy0d7;1kF^Tt4LqFnyb!5uSvlRS;gsZI6pa@INq3_~mw z))8}>oqU$bWoj`*%hOn}?JUJ3Tm0qR)g@V1WSBfS72gb<)_b(>dySi4Ft0JJgSOYm zJ#(&6-_DDV&4SY@&E&fZLEez(s=0F3PR-PCju8CMUItRI6Rp;XYpudo*%!H%L|W6g zR~!-Mg?`M&xnIpGtULA16_Ta04|(+59D>rdHmSyaj-($8^A*OX;cCP;8~}1(8PM|X z58@6dK933Z$^p!pK&01~8a75Pc zCCy}4yHJ$q7^)AeUPa#;HtPJ&F|ilo4XG$vY}fJMtlyglx9&}KE}|0^c7j!470k;t z$r+3n$RiwCJ3^4^W=<#(%ewc$z;w&m$pVD`nq(V;gmX2u9PF!ic@=lfheC{tO$63& zJ6lc4XCyomM>|!rU3!-f6|KNH?U_U=#EimNFf3vwN%VK;UyzdTM>&bQg%~c&&qx{r zL0tHyZvv#!1_kpS8{gV?irWSB${jHg3xfH@2r^0!Ygv)9B#-v-X0JwT?AQK z#H#&aRg6|301Iq$hAxAYubi%zfGUBr_8ZC#MPs&dQ)AC8nM7z>7ZUMxNe{*V8HWhV zti#FvXc(#!84RZT@%oGI=WX2k{9pvBK7hkG(B(H!PxMX2`0>JFL~~9nDz5JH594ZL zURQ~q{X@>YKnhGRGwSR*eT=ZAUz=vB46qO3$&GGbj5vNy;#;hV=~axH`K~YmwX;#1 z(^;dGJUdg3gqs06h<{O==B>0N4*v;`EgHKj@uPOcxLOh__-MW|GNMY3$~E_?j7a=^ z(O=K#OQbg6PNvz@WN15u%8%~%R(M#lG8xl1MmL`9Vg~+*T^o0)Y2NzNnLwNu2;i%= zy;E_!6rgJ#?|=P$v;^3q|L@tp|8LLs0V4nJ$;991Z~iNZ|2p6Q|6D=r*X5^QuLL?+ uXub-Zj{5a7<;IB);MRXWdtb=Hi_pRCUXznB=H#zmUNW`1P<;N@-~Sg4KhHb> literal 0 HcmV?d00001 diff --git a/docs/images/performance-test-workflows-per-second.png b/docs/images/performance-test-workflows-per-second.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4b61754bd45321b3768bf362ebffa4aa5f9e17 GIT binary patch literal 80363 zcmeFa2~<;8*D#E&)>=ihT2WL|Y%R7{(3U}Dh>D8V3TjnEkT@eUMCKtR*QY8_sf3m) zRfJTrqB5iyAPgbVq5@)w7y%(95yA{f3?T^_Ztj1A)>bVf>s`8BL2fGJ3!J{dd7P4XSrE;GYR$+uhb0<<~9f z1%FKRTeD@2kx>D0s%HNr@Z0-azumF^>C>l8O-;?s%`Gi0;Quc^US3|lzP<+!9`yJ3 z4-O7Kp63=878V^HjlbnZ0H0gC2$Ve##S^0J{!fwDe~R~hU%co0V(;&%z9&zfOiWBX zQxb3%e5k%>%Y%uf`+j1C{tP~}gTK%Z|HAP9<-ALPIvc~r$Y(u$sFWb`x%sYUmq02~9G%Hw_B0ssDBtNZ@zfcK4z{_ty@VPv$Am1|*SbUV|_ z74&wd04gxT9WkC@WF&uRrt5Iw|LG35bA4zl9UL5l=p|~U=pOr;e?Pq2Ar|3_3e z*26s$5}}QrMK#Vi;pM>ja_$y3v~@^yUF~U?Y;A|)A@iRyMA*&{WP%X`VD{mI3h#UU zBFs}&newUGS8kveZnvwEwkO8XU8`-d_4BY}ku(G7GD#L3dLs><{9P+`XCPD&6$#&! zn_ie2@cwOQuvt)eYh9`(JlWp2(8FG=A_mj2Bo`6-*LHedyO*Nh+T~J41r!-hkNNW- zb6m&UUUfLeKNYf>nlZaERfIv{-ZoM57ld$z<|cCeuvB9W_X*|)@9A&MS=N~1a4X)Z zwbJaJ=YHIN6Q(F_&0f5);eM+2ho#)uTk5*A^#J5DkK_Frl7F`4tU$g)>Ko;yyA&rM zk~5<$FS-mec7ce)zK;qv$q>v@sOeC2V3 z9#k*G@fE0|vUyUjTuFw*@}!~*Tx=6gKG??`*dpy1l0VaA!M{glMN?s~#47Gwq<@zr z%R8urJO#&rA0!LaJI@4EXOO7s|5y^Sa7k*D@H;A_Bo`L#bMpkN5|xKVWb%u=1CfI* zn2MD`yWh`NWi#MMRm=sD%=!Msu3n*n^Glef%Okr~Z6emhg+&y0Nz@#_`9q3uZCAaV ze>799%~Q~Q@Pk>N>8ai?x57iQ42Ky)6_!!s<2ZDAftneHF7g<7n8^>`&{daZ0$Ycd zZITTQwjWjm%;)ZaYj=gvGcIvFcLyIC;K|`HPTO{vkQ6w@-Mo1f=_)LKadVUWf_9lz zHx~Qe(Ag$#Dx=@M;6eibj7F{T%y{%9=?ZimsX)QY0j6!Dw2%VxH4t�(Dl zbEG|OeOH7c9e@-CgifoU<8}VG_qg7!zCO+j=|@i7rKU~%e8sUc=T*&5376~hl#z}+ z(YI6v*#Zvd1X-M9Ea_)~ef};o>ZaDHoPW3k=e1vbX0_tIU%To#Z0l@tIYJXP^CPl{ zpQ0r7vTS@=IX4i=?HcYF8j{t>Q>yH(y?zO6l_#(@N)x`xpL!a>3f*hz# zPCZ6%S({AOo(RSl0{EDKqew#PK!uXTC@&p?8>IG9oZ`q&{b?VSQD@sQQ(4?urlewW zz(Aby4n~1IO(u#&g0(uZQf!_K5U$)cEM7sTL*S!adw;)a$^^{@u zl}6m#*|6#c!)J1=H^rxF(*QlJ3z*2U-cgpRVHBc-4@EH5p6<&5P?oA~6mT1t|0K`aNv#T3V^#tXk3{s- zKf1$&ypRLcOj3x<4)+(2wGaY$ITpq0Vj9=JuEhm{Kl_ge`jnwUb(CAgbr+pDnVhH5vxgiX_C%B}s#zPu^<4&-6)y%DM6#)%6>4McCpDtXeMe=!+Z zE#L0>U9kPLV1zmt{CSCr#H6Bs&O-L7&xS;8Xlj%v<>vcF_Ed4K;e?9*yaSGI$gR_@ zxuUx0inko(*w~dTM1^PV+ z_N};BQEsg%jdl1<{VeTHboq53k4latQXxCpC~sz>spQ3)5um0G)3~SNB6@;PAgx-m zx(O5B$b1UdHQ-C@1!&spki)n$oNO*3Z#y!$Rnbycr(U&(8}J@f7gC#%T`D#a-&}yS z+_D`O-$3>SlrNH_0vU@2REPh_6WQxP0>)(tKk`E%Lv<71bSFz#8N8sv3|n5R=z|`L zc?+N4LY}qcp<1?GY$Ye~uw2vk;9?!7bU-~MMD&PGN=5J@PXJRz&J}3he#aCvUB>iJ z3ML&0P%osa&^Nq!!T9DKNNYpR`>${%B6p?P9u9cU{|>p+D3=knCxwwiwVrVij2a+* z>;ZYfk&KiZxPXo5E5Us29}5f1qh|X+<-Fj%!@~ZAGSfx*bkz-;QjD`Y3#Ygv_O7l7 zs6N5M1vnlxO-U85YC!BG$V4B=vQ?PApqPI~?r{cL!Tq8ERqP#{@4K%j!g{@r&E?q zsx^-C`%-Ssbk9ov7!l8fv!&{H@}e@Vaj(m;w3|!MDnnIpwzP$pyUQ573YSpXH@7=k zLd6yCkIhgl`~E@z(SEgJZ#Z$YmUfOC+2k)$SC$7!((F$JMj(wnjBHN8MgkCUz0-NWGa14V759+W!+PD5I|E7*UUP7C+3ma zU^lBAzF@|QDhtPSg*TGF<0#6*=QlA)&xNsfA&tZYvtp-)*sFb_U7r{{YsoxHeCr5O zM3TH?o@JPMup%61+}ymL{P*Su^2S*A5rysL+2!!^$G?5~mY#f;~@_vKqS z@Z`=?oO*yL>c2oB5yjrF@o_wDhB0>=hwO8Vk&>(a;F`!=&eOKsI;*w(nQH!#rP5u3 zzJ?R5NB;aq(ZBd0eMx^ldx)h8RwrN?)AEKw>+b3hQZ0roVXV$71$t`vI>oh4c~LT# z8Xp+mzg9gU^gJ$QFwN4I}nR$w}R|XTUs(=@Up!(A2<*^S8Y+$bD`nl2VzHV$BQjF zcYqPM-}CQ1sjo0D&0u3IY_CF?)sFsA{34mk_#IQB8v~lRRWWJr2zk(Bi`8Ynbv2VQ zlOtGWUv)_3$O_@jdZ9blhzpkrcktmBGIX0r<&v>J_$du!9lbhMMFHhcJthe1_(>Vo58jzur9*`Mu3}8i6Zm>cy6NX-_Tf~)5&Ke}hSeu@ zb4342Yl2Obo|Lc{5uH%1UPR{8E7;Bf)hVoUDT?jfLw}O%NKoH&SKMM*qusJ(1t=I& zQ>Z!!YA2Rsw^`FS(>yh^M1!>>6bdP*;)I4i!r>u4TWEVov7WS)`;Q$t|3f76s3k8$ za!YNN7x^<5NkcNIJHio_HQ)IL!e^2*m$A4_EII$9zBJ@_`uq~)51lZX&t=FCs~=a% z1Bhj&VRl*J_WRKNnW~+NRW(fNU`5^#Dy;wac&_J$GY=d@;r%083KCl<{n=PJvMbaJ zs`v^c0XVP@Q#ljDqka>9_DsQv=;(7x#fGC}+S5?}P2#USH?ISTh>2A9^vFHB z=~&}=&tvS%tQT{B6bjp@V9{xQL3Tfjn9WhN_5!>>E|mIw4p#+2Wy5kcmaF}bbaCf` zD2_7|4@D~;Y!4~DDAzHFvNOXzW=AHG}7W(w;kkp}Swlb#|zrU#wZ&a#p0{9U*cKq1zt1-OYWP{G4ANbQK-0X#dk+ zF_nY*eEXw}Y|Zu>`D|iTZ6La)!JgY9R|_Xk;w|YOKAFL8RU||qml+T^7>;a3wS4}^ zeAU;*Q0_}3W`9}p(0AqUwW`cAwOc+BN~!l1!I+h0GGDRKzO8~_dl{g@1;>LesQ3HT zjVTjI)CT;r$K{dPWvjyNy-4BEZb?OJp4FW^4?+;`OjmE6`7Qj4ZWTKcGgp@2`Q-x# z?LNf_b1<%QN%5gEN<#U$Z&kRmiugc&BXYExHQW-k58wk0&YaWO6_#c5qp%3MkBcnf z!)R(IpRk`L$RJ}?jm$rZWWTn-R(yZ6a3yZ22stj`*>-4IoIYRmN`K^!e%Gk)@QMR0 zW}0aRgOkCcq9+~iUhY_&{dB;hiTr*LV@L0iA^voQwbNn~q932EACCR?P$X0Yf9C#?4S+EiV;u5v@qMFh$`%PG4@e+9p69m@!W7o5NUW5n{0XJV zw@^U5y2wC&iI&)`b(rtSpPY+ZFX-%!T!%;qj^s4L9OZqPnDaS2 z=Rfah=xsi7VK_!EIjmmx0~zh@P?e9)=*mRXkPiyF>T7zlS=p>|s1%ZFeqm%|8dJ3Q z&UZ;T6pA&r46X53xBvW-%>Gl1oy-6BSGdZv_+5z%nM(*#@7zQc5_0&1t#B>Hlv6OQ za+7iy@vZ6@M-KUumc7S|(Q6M1HY1Yz@_>v;u8jVZ@N)wEgc!vuWGLCi)@H4C2aJ^h zf2}4#A&xKY>=Ot4$!C)Ts_&5@6Aj6^G znjdsSDvrU2Wyg@$=OTesEtwg$IxUC3kbRC|B#RzJRK*T82(TqiHl% zJ2{m~0+56PFM!e}3TexbnphVnJrFc8v|R;|2iDF59+dg>`GNN3*{JfgR2QuFY)0Be zqv6xG6MrkFP&oW+$1tHJMJ^urUAVC|&(vPZa+R-=5Gk*{_4NaihqN25X*Xf06cu#R zxhNMS!a?H+&&0;VKYw9V0-R9K#K2gdd(Z5h+k9|}Uk(mvCm4Rl6ZXvpu3Lx&zFq>n zx7tkb^)^5h8fWlz7EngRIPmp`f_S6nI$HVhfNr1+k`1#yNBgc(3Gx4FXRe_x@8*Y% zDN+hCn?wi3c~E8y6v=~*|33UF=>LA$9@jd)op%*V3pI(o8Z(-dYbzgM%Dem1NhOCR*-Bv4wid5;o)9(Z&H>eC%cYwT2PT6)T#N24>^Kn$<4?lz(R5lI*TyX!8tHld&}RYFY0Sua&=W6h zWjdmHK`yG8DjhgKKRjgY5Oh(SKEzwe8JKI2-Gk*j4a$-&Cm5Yre4)ndG4Qq4=2UG7 ziyJIME}S9-2Q3h4Ln8m~>whsjec@F_9}S-m7kuo=pA}FZ5?3bJ1a2la9k~_n#2J`1 zpXKgecKE9LnpbxEWUh5&3${)z`FjQ{6cHV19Ttbb71K&+%H`W9Gb#5h6NVF*&|)K_ zt#{Bvu8Mu>{I64OJ5*Kig=kB-xr-!h!UU20@tn9J0BZtJ)JCfpZ{F+b9tp*^>% zX#|2JRXw~G50A@1UTJwEVN;)cmr?p98-mKSt`BVDBEO?HWX_V6xBcm^C zzHLk~-6Ac4O7Qm%QjW!OHG-~kNpNJUeDTlif8i>IV?$IWH1U5?ba$b|>g+P|lVkkl z+eh~PXRrxEPtU!KCe0S)lLpcNeSgO`cHB|KYvgC$9`Wr1s{6H)dq+y6n_Z%tY~0Z0 z*;dwQta%i6i?xdmifRBwZTFV2R)&m*Pf$_iYt-MZ@s04<>+2MJ(UhAt0?GFe4;w3K z%~s}7xa#zD=PbCn56cmsl2zb`I!$!sTDt<64TG{H*+(;JE)K_#dZ?j6A&}Q(TMAh_ zvAjUiQnYTesz5MVns85jOaz>@y3SnYVaw!bZ#ZV^=*U zWKQ&r!p`aaNJo$74{QjizQ5kTV0gu_Znvj&73nUYmhLkizkC|Mq8q=L9KVeHKl%px zk53UUdZB0O@|l@d<9ZA!~KF@tY4APtmEN z+jARJjC5c8xc`kWzP@z=J9y~Dm&-r-tjB2PWL<~j%!vy>Za316oqyrCch@br4esr4 z+s+Cot=TSDpZt;fa7( zgAOoDNZVWx)V5&b&p#aLe4F}H}+wHoK2LN8DTO@$v zHVqiaiC+QW=k7D!&%KXF-Aov^+wqU)yQv|rTGxleQ+K;|F={6quLOuXaKiRf1SNjr zL-Zme`r(>y6vX`o0AW=B2k3I($hvSA#7xteYhM+ z6lf>=Vlkh8xMJUgnRymD`v9I-+&25O%**wibv~yjt-FNSZ(dn~h&409rcQV@q;Bmy zpF=-Pm~1Ms=$iA+Diz_D{>Orl%kzSfz> z(BgQ8?oU9=p||hD`+6p64jm2K{F82-Z%yd1?^WMt&ldd?{*DuWgK{$I=?Ss?4;6_?l3j)U zBsG)U9vHurLF=Z?japZU`Jv5Pw}8pNO3VfYjgF7_fi5kYtl^HzyT2UkCg zHdaRVXP|`Y)pg?N!Tn>Q!O5BP)awg+VC0V|SkO-dE1Ndz%6@E2;nk#RZ^)}=^9JVn zr4#E!V~yT*E1t&ta<->?G46gjvr@Ds8$OoH{C<1f7!ZLzad=7B(9n>uCX0EX9{Ki9 zw)cEDgg4Zl>Z%bi+|?#(#`*o)ok+Rdzjcb~@pUtkUa1OAH!N<+My?>Ol@F0qi{|_= z_mBID$Ws&UN#M7hK+M2KoX0B z776VistT3e!l*2SCaAHns$*+YwdBLULTn@-Zx;3Eqy^jVE_$UMki!79UPd2Uv0RG2 zoVb3`d$}vx*Q3Kt>?&=q;_qbP`AT8hsut`|MM^hGO+uUS*MIU#&)GYRd2<(x=$(hh zjs2my)!j@q2Ya$X8`dAL{(a9qOH}+)`97yjD-(HYm5TY0WN0A&+gW z?U5xe%H^Z8q>rnwJu^h{Y(3jaXdYPD@1~fwc1(uRG^U8tx$ReRPv{HbK90l}Qtlj+ z5o*i^+f><*ChoC~8>T$LH2ri(v^uDu&d!7%siWZ0c@uaGvbuWJ6jB&9{2_*c-) zxLq)A7mV8lf7u1MKWt1Pcf%+5-m_FZ7&T~wV%wpX3c4iy+LMjVIax;p9hK{mbveTw zywXtvUW((Lf|`=5;K^xerq~vuBa1M^I!B5#%5Qxch&B>z_m4oFm^=$qd1pIFzbH`l z40m)~#Y+S=4W9cy8mp66o`os#;t|RrAF_qmKIwjKWc_e+zByL=yu)?$aFt`mF>D*M zdsJge)fVX?wVasbw_Sn35#mQVF!civlKk%ov$C&=jmf z8R}=OtPl4XvSZon@=D_7j*4hyu7$W5vk!!67K$|`%F>TX#VCN}(K|HfTA+y+v*dZ* zAU&en+i?TZ2%=+5Do3T4blZ;gY+4W!Ly3=j3bIzv8GmqB>W$qAoO8yF>+x=>muoOX zB2af_c|V9w|+@fJ|BcB?v7&I}<5Dl}%0uAs%%9%perED8A4-?!OSOGp#X&1okCJ z{alyYov}+7twS6$S|{XkKy~`H7%mz_-nK+#@dxG#snx3(|HFtK=F~~=G7lO%9J`op z=}k=UIw~cK)6y_@j`y3im>zmyKlnnoiJTR$g{;j#xtX?N<%{^QDzjV7%LtMq>ia|@ z%y?9>Z}8BIKBI0t(YB+++c~m=L8GlacF_}N9FI{RQm@g435^x~?NIe` zX}pujG}$q304Z`E)eik5{@bq6Y}AeU7Fdtq!lg9=tt6ExFwM^Hh8dAdMzzOkY!6i_ z!9g~*D9T57gu}v<6GpYqw)z{#)Z4Hkec|W^8={Hu-VD#+?_UJ-zmYMvI>WXDJ|Y}$ zMvE@lFmtqRcSbcgpMb-W4YOO@*2v1bR$7#{z-Tlt@al869W?)Bb%Lj_9aF7syFBWc zJ3#dVdnQHOc8*=CzWL=?1G2^B_`t@LR6WDd?YBs4?R)TT4Jq*7S#*<4|B(uJGuI*w z9HTUi_C0Wk*S9_@=L<#CjjJ`<`APb;M$de3JHaFE` z{YaPyl(QHT?i)3Er4NAO=O*kb=x<(+Y@R&UhL&oEu4B-#9@vVV9UqT6;ITZ5v@{e- zF~9o0)B{dUxI3Dez7=a+N^?@jspK6U!rrP3`y3P?l7CMJTFtcWxIL;{Cf6eMfAzPU zc^2I$-%O`|1_l&}(Rz-~rN2Ek3zeu!FfPGy1CVg*s7U)F&%#>Wa4^mG5Py`6u2mq3 zMA#k@cdOqUNlF+s6>2L;(K*Uk)ZYPW8($6`EyDp2Jw3QMr5hGhc8(?0EMy|W0;;AlpS%l`yJb5J)b6hF4>~n&Pd(IE?N;`T|r~5?p)sh;XxPG#UNd1GB*_j8m}s zDbhE(1lsNT*{G=~V56+~q&b%qjOa0We&?w2ZLHD%r@!s+b){$G`%w!=&5~ISD!6Ed zI!e?VDv0hpg~!4y-FQMM+gTWY~?5Ss3KvR6?#Dm1}_dX)#^xA}4xd>~Ym> ztz>jf1@^7Z-n*Kkl{98Ezvio}#^hLk#%KM0NQvUA|8yZoUN)Nd8E6eudRGYY{6VeG zq2@GuRA=ZOP_zeVGz9})6h2lXo!X;rk=}yFpst;bDb`!Uo}g)iG=HAVWDG?GsGZ_} zk)jO|4?iayPaJiJ9+*AWwx(2}RaF_D^!-?nnD=*z_X3}P^kB$PKE9b$ z?`ksNF+eh^fdX_NsIlAC5vD%p#b^1Vs3<&F`=D)9*4VMf)he?vy(*uB-U-hbi}2F; z?kZW9LUjc>Jq9fmfy!fqm=^5E^R0tdL)ZIJlx%VGYL+G;-88}7sI9T z3s#QCJpa>R+10;;0!txM8uT>wiH#VD)zQ`d3$bdguF3=N7)%$XA0AcH0!sp13Dn0R zC1D`HQI#-;Edi7i@T4kWK^}GsdqE3T7%`@%I-iUo98<9?aZzIt)!+^P%pnspP3NMm z9Ni?=>9xR%z=%7??rNmgt(rJGgOKB$`cS?xV;ge9`H!I?B8~{**{Z=&k@M2x8)(Em z;Rnx2rD!FiD+C~Cc!9;q-S*@JpF%YKc~7IshYq7(F#*efmBr-M(^R36gqV;>xx57K zc|`xL_FVeE`MXSJN|52GER8+fZGLUTUC_HTEmBg_`H1dueyROhUD<}eD=Lhn+?p?H zXQ+|nL1X}A&)P291=t21MLy9Qt)l!ZKj?~8oJdGKYFs)f-KjY3x zSU9a)7f^jx%EEf=$S|@&c3oE(@8f_ggMBM7Wkg?lA|uLV`rCB+ zY7tiz2g*;`FsnqBK=R(i9bY}hn!K~&4?~OMk#>mxxLx69^KqP2yykyZIyhnihHsjSRR`|`8xB3)5kznw?CWakHl1fB5-xjaf+Vf zW01=#vAuUyc@nrOtn3m96?LmVeoMHF)V_&MOl(ZaGfl4$ND?TK#!7;`P12Q><%(Nz z_bsL!DIa4pv)=4h1TWZ}8pw!fv>1aFhv;Z=6EQD~Y6@E~7-L=4MU-s#gIkh8H>Bg= z#@jmtY06h<)3sqHvmM$?W+gkd?*R-Ec?s_VUMd;v8mhfhvFxhgwbR30v+wTUb1f#A z%nWIbTNUiYTo5v0R$*d@ZBxp--T&IMG37&D8J#y7d-uUFK-o!9MrZp-fur0vti6D5 zCn*ZzC+>Fr229qN@;!;Knw)U&^^<|^weQutUEj&ISgb3JRNcs;kl?J^C{QJ_rz02X zIq~`~7Tn))92Kx}8xwo^7l+F_xlsZHW7D;m$LP3bh{aRDRd4FZ7ZaR0R+lYB@9zTp zX%w)ryCYUO{0M8YMH-{N`-b6h;OvcOE{!RR0;)l6P5}EJP;ZY0-{>_Z+D)ydu+!YO zNDFc8{|?trwjH3dCs<4#Yl}2g-2)$)8beWbH!;lz538TN!Deqw1dPUTbx%!0&d22G zVy?yOB$>P7qt+_3?T3t&xRE9F#~M476FjQ3fr;t7a)F-4weqC}R@N9BW|lC0o!!(L zspyWrWusL>K>$*hayFfswBU^@8I8YwAUbIL`K@?RniP=5Rq1tqUrotyc&m*YOk5Es zPP!#$y{ZPZz-NTs%numfP*NtCE+^F?r!IW5M~9}2bkdy1jJc1spZ-LA%NF>n*9GLc zrz|nxhmsRh_Ka+ICxtLI<`&+sx|7Njpg3Qm`hw=t#Wb^WvwsXw-?l9V`M>aV}8 z|B>>hfAOx8xC>xt-L&1eCoVg1>8)h0*BAGe<0eF#)vzZXJ@ef;I_&zrb;>^keC2nkLCB07@2MCH67JWcb-87=%-1#q!2GIO4-j6f8xmgw zN+&m{6d-IWD-ZuwEo$?Km^z^}`3vj$I>&(jMJn*EjNQl|oKJysFzv7EQeTdUy?46M ze{FvWa6^-*x27>2b`QhePi{`r(-z1^K~X%^LN+zS^i?h^Iz^W zo~QWE;Xb@ISy#dApA_E!JQyC}xSW%uJ^Ak6i=FdFJg7YeLRmh3ogVXn!%W$G-9IzV zh0}mMxU>948L~|cq`ejAUkKUTnuNay#1kAOqFZOj^+B?&FDix=9GWm|Xyu|gKw9cd z18v0%wgV@36lS=b2$Rh}cyY@GVE*1$$uav1n2sZL))3wZzk%(YH&T17;O>i3XrJ!W zz9RRWTOcw5y=urCUgeYxUA9l9yMBCpte z%;^EQ9eGhq?ZX#E7ZQ#wu_b4p)-`ygkSB9RhBfmne8yhi~FT=oP;=G^S9p zIixv*zpHy9q{BGHvlA~7`)>;^il;rbvhYL~J`V!rm&_N;7=7S#jVWxxl+rz}bbF@c zGCt;fn>DbowPGarN2CyGefNjmgEMAsc~P?OAK22dQ}YpIrHga`mDD$?M`8 z9Abs`19-Mp8VhP5U20Q^>f6AofgSdu`rq8QR$ZryX^NngmjD42=)xn2bKf1Y=ze)Y zRBxjdTm}u^J0gzOwB_z~rIYZ{G-XfEBj0HS@t=+{4Gh-Rf%w_8r zo!1padlLqod~4Hq;LbwQaTT`mB6|2nqBp_Mc8@(KAXZHrz7?Ot%X0%yIaS5)c6~4w z3^|4InqgV?b+on_c>pMAc7xNE(%(1U{ZQw6xNi2k5#Z?uUcZCF+XShpscxJE)^w3< zhR#y|l3iq|LYIX> znGS`d-$5PIF%bo{_+d(Tt5&pI_;ofcaNV!JeF!D|k^*>(OvE|2axK=|5|0Ld{$att zQ^X$EOgZpp`Rkljm-S%@fdAOWLzUpkvt!n3+xA-3xqEnk*9j-rDASk}5@84>JL4fr zADhB-)`W6f*ibENN(_OIbEQ`V^6%3n1G&((2(7BGnGAnDAH-C%%(m~4CU%`)G;_1g zRrGJNaWH~S6QP3f+%JuxILQI2V?K9aDL<0%LAwmcOY(35hb4VmNFoMf-$&c0S8}u= z^;G6JlHnn^iXC}xrK%B);}M41E1T8w?vw>GYdnYyeqRq~0PkcP$$tKy-C7AaJhj+( zAj981Vc2=l!%eJwgU=^*b1Q`&Yg)C#KY66P!R0y_C+dL_s5B8yPDgW8gC+jSXa(w$ zkS-In+)L2j=~RM}c7^z-^mT(r)PJb2>G?0%~FoD05~vu%nLI|>h=$=(ZkEQ z6Sf_A1k2q|@e2g+I%ygi9KwXgE|jjqoqpo!RI|}f3HKWI(ouWymm3t!n1+qg#C7o=S?h3!_io~<__Xaw;C04d7;)%=d?;tFs+5svBF(J>3o|o)D`Wy; zSC+PIgSuEjjB;|gi`Mq5VMr^Si%>@@5;j1|3@Kv>N%;C9^aK+9rwh7Uv~EGv+80F^ z0sBBa=x2r$t7bVrgqk7I{w@go2HdVCv(D@udh{}w8<-_hWz8n!`|2EF?ZWKz1nx<9 z@azE{gi>qwOEEDdq!bC5*M;su_QWeq;qGBaTaS_xpX4td8RpX$>OxD=rfM>f8=x2? zsPouuene|IyP(HnQL-Z_ifFP>f>K?^{lOGpV0%A-Xjspm{J7?i3GhzEp=?-{Lk5dG zW!vE^MgOa~Hjn+@eDz)Rmw4mm=u)p`Wu)W7A<}(nXUZO!TK$EK!`*vISUZ9dq(lcP ze~2UyKXC$Ou1n4*3fz|*81e1xI6G{u_85%PHUZa@6r($ZDM?%JUP(w?DFW3fox|^@ zM`2BD7$eTR1yqxkg@Qg|A4gZYk-XI9!geV$IUvUI8qke=_7`ACpyPBpkjl;PJbMS6 zdY^F{7Tsrp_wRVx`d~Amr;m(Z9wp#LI()9{ywPXV|Pe*-mA#$Km&e zs0)-(>4yh|}){x7w7mqqv*jg7B`IG_#Wq7aqQm2OA zQl-F$M&Kc5?twfRe}(ZMbsoThwhdVpLkilJ+|4a-K-n|5A=$G@53k2u&g*>e(Td;t zS{MC|^?&*bbXwo3T-#`&@gvZL_+PjkB~zo5Gw(=jN4V%A0xZsWbI2 z51bNG`-=pwj1?fR2&7#ohi-D$CA<6bR0=DXK<)KwF>z&T%Ht%D&z{g8HHLPcnY%S2 zcxJ(fsq&@;2c&mhdRMgCYB0$y9pBd6TbngtGJO4N*XiQ(OFaQWmX_c#4$2Z2?}T>je3K>oZ? zWsr0wT=|~#nD5bm($MwOuCJmd?jA9r-!!p7sE#SQd|5{fve-+AYXBzK*9Kq^_zA(o zS&|_^lUC9ROe{?v4t8GxE=m^mBpqb+S;Let zPyM9rZ`?erlyrtL5^bui(aYM-F47O13@oIH>Y-kL6}YDdNSDZlFkSV1X3`>*+OI1| zKV_=S9;;N1X1B(c8^I>Pd@->GFV*WI*<)l23?Dp3&r?E-T>Y?K45-c%pOf`{hYb8^ zfC%z-jbvrazrrm(?h+E!Cp2Jq;Fggu-6-u~ZYP|G&DKA@&b7)} zlZ-)n(7tE%FZL$56|qmSik3=n6tBc99fOp4LykMNOa-vI&< zGauM(-E)9p3{LiMohyrMJL0xS*X9!xuz7d-)7~Jiah-Y1HLY0Z%v58m|~tm6O%3)fa5(NbW)v8y3Dg7q7@!2 zZ|2mXWn=F=h*xU&XPoefA=D!eE*Ze7dt7;u9v?9v>}=kV2X9kzfM(EAy}+=mSPBMH>$&2&5<5dl&> zBeZ~Thy(&R*dU`JEv;KPXRu#Uck#0RnZU*?`rN_>OW_B(Vm~sGL!<{ohwJpCO=m-T zv@j1g2}w6yJPhDR5HN_fgjMx8d_D4~0rcN6a?k7}v`;x}YA>F3fuesgfR917d_^{> zmT&EWu0wE#g7ohv@LJE&uakia%+gEV|I3Z6O5@|u>MSk)q5<66VK#Uj`P?FE_z4mq z-Kp28O!IM7MF^}=YAA@VpBR1)s19LZTkn_+D!QOCPne6FQi$15IL}ZZ=IA$148r;d zuAKMYj_QO@>L;WQv(U#jsA8fEiNJN)ecqrBPi)X-lhNtdddv4+I*mfME;Az9rk&$wL={_kD zGlVo6U@C+CqcG)?$s~gbqcfe*wgjXPcpE@Q^J(gtLB2dRhyFoMsLLfp3txh78)i?S#{G9Ef8~Pb3*TPYM zBOvQB)~d<&E(`FzD5j?&b9c~UVtMF+48~BH>xX(+bmSJ9F<`+b4NFcNpn8A;rOH!0 z+|n)0Fu%z32>{&%0ZJ?a4hYWTjvhSHW&qIvpE{usbE-jXfzB-QuFfKJCrb@*Soj&2 zIzh2T32`pL)Rb;2?~AWI4bxLe-5lqRAQ0jz2Ir%Ey<$JV5j+^KyvcQSZ&}R zH9+ftx+tb=FLrhKA$JG#H{a<4l4zoI*%(=q46x5jL8Pa3N3<5btYd?I@(9&MZ{|>| zNGiAaFvHdWpAL{7+Z5|}N2&h*Q6?J|CqED$(SK3{YNv{gzKED?C+l^sY|ghx((g<0 zGj86dS2r5e2;BK9fo!EfC7Qi7YN&6gXev2n`}Y07}S~zP+5=m z{aL>n1BR+DbR-X=83r^7M10;fv~jJ;7WO4M(bNEMGH`3g01vY7-XYYN1`sefS`D(n zn{Udv;vD3hfeu=y;1676j8z6W55Vrot4y_`gGi16I?OQY08w9Y(4ONwq}_lc2atbr zZt4|72I;K-UZea$vq9N_H*$1}0U8Trn`T>9aqau?;e&MOm`HyYpjAx8s0hdEMga?E z8zSy?ajY%YAA4qEh739>pKDB^az&Dk4q0z{svlR-4U->w>1QYq-jlo6$rmp~tF(Pb zrETxftqgF7)|ow~8x0mfttvp*nR23EuHOCBnHTitwLCeI>JpTxJw=Nt^UL;^WEPX6}9y;SI?%Ec zOY~oOgY!u+;Yiye%xwKEQk!pqcA+_m6e(x#RTwt*9zM?*-KR+T-Jtdu*FAn%k_=aU zKh)O%cycK5ftMfxe3kDyM|x$WexX=odsQYChA)W3I90;(R0pn8>K$t^1$mz(n(Cd}&1a&mk-P{izt6I_a23Q0iHKj;B z9I;!mnKaoP8N(hK`*OSyXi6cQfIQv(z+^?PUQ;!v0K^s^moW?Wu-wxI*;8}$Ug$ms z-stWoq8dIZsL%1}hONI`qkqpp(v~KdkKV)LLt>I4_e=w3p)euE66?Z37WOg>qG}Gg zY863|g+%Yy&s%*ov%w)@+9dki?J|8aEP%OOgmGy+KP*e-XR6;bXH^a^Rx@8vw4M8T zDKpkvzijB$5jDH+i_jrW25J7Sg0Wo&`S-z%r$o}gWSM|_FReGC$<;5kfs=C&+JzqX z#ClQs&W=tEztC5|!VUwBgB-6o6W2$&Xn;x30%R6PZCV9_$PXfhsCVIhV+UlY440k= za<-Uy)M7>arHTsewng+M{d(I1e0 zwVV3D^|y8a+HZh)*$!gzIexIVK6bDx7a|y7!`=cVRx|8R+PGP}XnIJPM7_!i=%G+) z(QXf3TrbJn0H^TLCTOBaI_dm!>#QPva;Ur-E@t7?%YjS=-~{a9kYzFN?!;7Dy}X?W~bd z(z5w#BDD_ff_8|F4OKU{r%upsii3v{>i7%nE2!OY*nAQ;Q%GtCITu^?9{B7*3PgJd zV>ifu^6>Sgi%I?!J*1`RdHQdvbUpiz{2r|p4GgerBTI)Lp>f4Anhq|tR!S6}AJm$7 z9MK=0$pn=(8l8{={7?+VdjS^Z>%w(vb%CYe#-*&sd=BZM{u@c~j6-OQG+1hy?%d(0 zxX#_^5=FGKMhBTgfvDM}Y5EU%>9uB|S=?oHWp3n)T_K8nf_9MRUu1H|Bi;b`vBv&< zb-`X&bD`BdYfuGk_>g{=R;vcw`sC!(i%bZus-JVH=|n40^jG?07=sC*n!z=PK0;_U z6{Ls%s;F1(hND(a83 zN#7cPQ4S#HLjTNQzDN>GIT&uTO6YkIPZQMqqW@%D3f}f`bS<;Lj`7XZO{utMN?Op4 zLS(QuO_wf)T-PN>VPvBuN3lkdqk_O|A4+Ro!kETm$$@&;7wKf?tJ=|WSH-@syr~hm0O{75_%epOY4VS|(K)TS20tP3p$iS0>59+_}Y1`$|4f?HQm!gip~@p&=-y&R~d zo^IU10aNWaE^X5Mds=Hk=5Lc{NL*@tUF$&JkbneF<`TbSz*`FJ7(3!Z6O*KB7&`)C z$OweJqdmVfSAY_Q%hA;~{&%Xq+2f*t$anknt!n9SYv^6>+ilYej@RC0VxkQS> zGp~|6kRWP6%)viEL6hjHqQsD|!A(G>J+r<^d-ANDvkxyqMWL6&^|}Pe@ASLjx+wAC z-qmT~MWK7!*tA|0*kVxASoheCAv6rGnuDg6(&9iynJOWzdkf-RU>J`cHuk5cXhHq7 z0Ddw-Hfc8&l!;XQW)P?PG^RvORmvvM^j&~${81`u>t)CcJuGC z+4DK#8g-;}K)|Mu{K)nu<}&^T>hK}`*Uzor0!eW%Wv`fkQw^)Oe0qAW5&0o-KXaD-!npsyC1XSO4cy#ke zAJAJqXYI6^2TskHlxxvN-nH`q%Ho4>AN@9ETe9t;XA6$*++YQIKSLQaoIC2n#VQQ zxMCl-d&bSeakFsTY92pSjGwv2kEG)#_Hl>CxW8xI9X9SQ9QPKEJ5$Gf&Eu~4@tcbA zi=6R$uJLQY@mtdIOXTrOd&Q<^oHAfm1lt=$vWGsRNJCT`SxX&{Ko$FrHl3U8_c!v#42*` zftqVYM3ggxi)_)VhX={|zv!jI&7eF)Ol$EOOCeMVUh;8?<9g7_;+m~0R7p#1^-c`l zQ6X`_>mar3)yRHh|MVJ)tQX~1&h6D6i2glZFPhGSqOwt;1X=%aV}DeJ4>VMS@7m0m z;rn^Jz9^qGP-|oGCSz@5o7vzos-w5H)#Nq4awOjKjNZVW!(OiP>5VBlsa#Q91uYk4 zDMYuO`$BJ+(+^E4DB;N@*&rDsZgDfXPvzHzE<*GNHo`0@~?Wt#`k$&{DiNf-tJtAL~M`5QQvBe76#6KTx><~9E7c0z4!sUY*ituSrH{^}% zA+V4?v_+asq)KJ0UD&EJ6NGDfY?U>eCVZjcXt zbzYZ<49xlPK+k1Y4LP7!8zfX50{QmECeumpJ$CuXSLwLhRl7@@z7acRB)aT1Q^nQ3 zhrc=z>PGtmu(suDw{g&aE`_QZI{9jTlWxI~?+9qO2xpMo6nv>xd8g|F!p}QB9p)qp`N7R*?=`6p$3HrL9UFN)dsi zEh;JstrH?coDmViAcTTQ$sgvY2o5G2iH8z7NX(<85MGUj29-2J`0c z-u~Ue`HnjC4Tt9YG0nHDnr~M%-_2{j+1dYxz2F84pvQmQkl0q46K@x&6kb zfnHEg;p7%k5sgA|h^(T#Il1o)bPTR{sH0qp<`gSZ_=2oAgH%`L28X6;h_veDy>W(A z)}8+ZEKMBUCj#+}%nlC5ym8gg6c~pbpx*NoJ@ViiQ@;yh?QsnvKj`i7ey7!hh5m2M z!R!;{)(z{@4E$xrPR}t;qv|6V40iD$5D{p=#Bee_--a$9aHip$3?{QKqtel~H0C|% z)-lc8oqk1erDK5uLtLU;J@;7X-;J-6O$Ax|%x916fo{R}9&hv<1LNq7a0Hxs`<=N* z--p&j6**~6vn+BUl(gBI_91lp!Fq@Q9uPL!5H=f#(>T_Wxxt@6gE_^@6mqqIM}s(6 z&V%m4&TJApJ*T>85&`w?h_S-;b0fF)RJnzaMH;m!jG-MqMaNr`fDO<;F5>pmGc2b5 zMzUC0idncn#7QIPm9T%h5N!Hx4g^@<&3JobvT@ir5r;L6tO*XbgKnPr zderGH=BPkVSvA4*rxcjEX`Fcv%&}w5}BQ zC>p97j$-<(F^WWW0Ss;wjYET@ny9p{E_O8q6T!k5MrFILUy{xmev-E;dGWT{5pJ6F zvB|2LnwrfIXRBgI)O`#WEJ`Jk$kJ-SGP=kot;nqngXk)T59X+3QNdw9V>|?8`V%Kj z6v~IPFRa&JQzaQ`u|P z$D5PWe~D>7qEDNMRnJi^QFM4^%4pe$T}(S4lwA1Pb8ONQL<^1UZ+JW1BqtTR zKy@r;bCG)#D4+@b3W=?7K`w&FO}9h)vj^-GjC5Q|qea#|9`*M949icnn9g8EM!iUH z>1pk4)x6^IbWaaf@CmW3%{NbPXPTZfX6S}R zWPAk6httbD{0PXR^^@Dp#Y1Fk&voTl{~kD>D1dJ_KUXDa3jeBWG#(Dl0MbHY-$UR$ z$S@@7RYP3WY^Gt;@C}DEXtl%l=L^(zWJd}brQ_a$TfdO)SBt7#c51jO3}nOiIxv|t z;lPfqUXGfbI7_FIf6wHMhOV=!p~96_8yy`Tz>-?ptRFW$S{O3oNX28V*5QZnh0q9S zjcJ@5O0`5_=h{o*9Tz+nb(>x)neNb;pY`V>SBCmqn^SC!cx??B@mGX@P?vyPp3en0 zyDr6HlRo2kRg?y^&LUMQ!KQ`d^2}zf=Vu3`Sz3I*BDUP~80+?88sUo& z)(Dk|v3dP6J2sJK$97Q=UU9!g>X^uSX2vzkM2Aj2Y^7VC>FQN%h9g>JTO~OO#KhjF z(r1RC8i?kmo|yjR#}d;*bDuw<*WbnVP&ac9T}6S1+insq(y3$iD;s=D?U0x$JTNqr zj9PX2y&BLG9`N#$vOL|$8K{(_S&y0OJPlmltZ+)ilLe}y*Ndy_CPz0Yl0fssMzpEF znN-Mo8{)MTTfVW7v5?4KOW2o*+*A$RHuGoFQJ>=i5N`?Hml?bZ$FFE()A27z-!B#B zev?6?8i~CgsOgcu@pc|H@SJ>k1jNnJ*e&G#YC+uJAB-q2Psj>=wWGWEq9x5!xJzq{ z_r#B6a%pB_ji{mFixzHN;9#b{FB9$mC^Bi5Zhg?-C}@us`_qQt`J~^bV{C@4zq|}S#DCX}g?f*q`V7KZEEyLxo7%|CXlKQ=7i4m3sIEHh zI}XBJ($`AU8J*8`6+GaCz41U&r5hOcnZYTsFYdUY#0?c}nmG)SZ=huFfhml3>lSOJ zc4Y!M{laXA`U{Tx2I*?l>W}$&up#NUqQ{=4RJ(ZM^Svmi zT3vbqbgt^T9M8{y0}7NwXs`%Z0@TU_!cyP6cn4YwhRSFT9}Ie%I9(=}Y=CPBHE9 z7@H+Xm^Gm*uNh0L>WfiSpRujz@0}?`tq*Rq`^=0BlDDph=y~|V3_FzwotE*3=NudD z?o@vzG*r38s`qMU9~I%JRPMZeC+&@P5JOw}2yyg114M;qA=f{C2Eih&9&g@x$gi0pIj0e@ws8^zJl zQARVx2%@~NsZ;HvSr>4UKVHPNPlo1S2bwbKIofh6{)*CdE$+}Wydbw6w{5f=@(J&u zT$w&GsP$ymA=S-)K&=PDyd^t`=!h2jZj3}T-WWI$$jH#TGsU}%U)xf!zE3UBS)jJ{ z7n*>PCgdHI;}`MU&ynhZ%Va~~xD>}J+x=B9qE6x6IcKC zti`P>9^!`sTOc<0^q620%gUH4K83q8xr_0W`5YBGR99!T_@n;PrZg|?nEm`Go|9%) zLCMM@g9|EtyG%lvW$cT^<9oX zE@$Q(1hmzqY`sB(73S&`5(BM^wMmeR@_Y!@E&BT@!V)dardfhD&091UT5A{V2zJIA zs=OgrMsbh&UefhxLQx8qkVY(Iq718L2Ocn%U>>E=;2?Lbb#pwt+;^RPa{RkX`U5>y zdSp>6n&!4LsI?&3enNH2`aoq%m8aS_Pm#Nsp|0=mPTV{?jAC2Urp+NEU5t!@SD1s; z(V|>crJK9(nm#k)K;aQ*h;^1b^elNW>N!@6+MB9xv8|=LR=bBCgOF^5#Qhs0yd3}G zl{FHx|B98aWM9D_lB0n$G&ghG_31m$ABq!hZ#k(-yEcBM5li%o?P%zmW|4m_Xz-vQ z%%h){)7Xv4Gvne<@>U3QBg-9x9~9rVhV^GRLwp1;_}XFhDw7j;A#lg*MhiexZ2w<40wI~1u_W2F`(QQQYXZ_pzYbb!ddvfL8S4?_H%2f< z=xeE;lrNN?&G0O>-6`D~tBbeC`3(+Q-{vgw-U$hP^Pj$JOz{vs0(2MP1LZyXEv)2N z$P6Bs5au3M!H3F(*5(Sm3W-i9HH!RD61hW7y=`ppRk3n2dvlW6t+}c+^vw2B)czX{ zgLDAk@g^=1*9K`?wi}`=q8cwLk3mJoOBOZyZt=y(b$ONw&D+VJiWwQD=bm}})UW_N zk2>BZ0>_k{MqCEvBPK^!Z@#rG(;L#3dW!Wds|olhHKSqcQGe6!4xH&Fq`lfC6S`SB zwHf~fHwzN{(`ZQc3QjBzO$%fjnqp2%cPaHkEfuJ*>(m>d67mCgg}LlYrwH&X&i>`LwD|2CZ>=o8JxJwTS1)58hS5UE zBs4{$vIk-)8^2Uy{y`LqVbJz($N~ zwcF#%6SQ#Z$ajA{rb@$D%Ra~T53pE&*-n+Geq0NYNU|o9<_Zq^ucv+fy z&~OC!9b^0c5$d|1%h!7dJ%0#p#YHY_KZ0?(>ZtYA4pC6_MaC<5Y4XG6r1wE~7}a7t*KA;)SD?6yZb5)r zw;um1>5v12MObh(aMbjuUKg=7rX6L7#U*6vy|DeZf@xy58C9BB=ggle(hUOd6&C_6 zO|#F)qF)SuC?n_X#7ZaLIG;Vv~#CZv%P zjB+jo`S;<+i@lT&Yp&)z)@v|dEzsWWB%BIqz8VDQVD@Py89a6t8m_i z^QV1~e_JI#`uRQjZdc^iXx^ zhsaM^Dtaa60m5{tr0k1Ur^FXArbCJpKT=PdRwj*j0k^C=Q9Y;&YxKSP3^4`2#Fz{m z@5{`}!h+u*wczXy&T+pgF39l;es`k$tRVXBuE#Q^)$}?{DcxZM@m4;IQL}3x3ZU?zaO3XHk_KU)ruh4*l=C!8chRkOpq6BY7~&_f@at%Vbq(E&lPX zTVD&^j}cW4f2IVxTE|^uUCawJppWVCcuH88v&w;*Q%yZT^Q~$3-E`^8zxiHXfHBI! zCM48nxJYlDu=k$y*Dc&2u+HpUx10h*MS)`FR=slZ(f2V*>8iC?w~ z&PTJh+6plryF$m+TSt5+P>+<8kB#MosKc%%{e(pa9YW|Bijf%%GDPHB%W>Dp>FQ<{ zP9iCyvi7M{5-9T6Foo^s$LrvK({*R26mpGFSJJx>`>KQ~hyjRrz^c(1Da-7Bby(>0 zR1U?C9u4_#oAMN*oKgLL{F#|x@6U&hU#72-a=G7}VEfLl7A`>mf`NgD1Yg_lp0V+b@7(jrTVRKA4L+I-i`s{5Nb2J>!$%q)NvHFvz07et0Ge z3C2Q!Xqk#b3$eHxg_42Z60vejW?BGy@G}&~erkMnPTwFy3)k5WyvJ^ag9g%36byFc zeg^E@O%#6qucVVLY+?e*8p}$}94iQe!c=l7{JR(;z#VBVwWi(&hqD^RqB@dVZMqWe zJE-UqWO27k2ITtXP;B~NBTsvWPoLP(hmsUdrI@p3X0tp`Susmt_m0^wxVV2%cbmi} zJtLsZ4%?hG@afJ4-!7kny@Q{3_=SW9-#aIm^LO-rB0>N<5Y#nF+FW5=Mqa?PaJVj?>Tw*R>>o1sp@O z26mA%7p}gf#Xe~=q(1CrxGU;9`zl2JwiDjG3HgllWB$^k2B{pR0h$Dxx-P+QmXtVi zNe?_Jum)0+N^?+g)q_*$;mHo3sO)pgQ%7Dv1>G#K2D`n4B+x+Nxx0$3f|S-e-HDII zO=!*_)gz>s-5gGX!7Lxot*u>nxKQO28NZ1++@=(#NojRrWiO2`Z(!sNzZ1_Hu$rO* zmjk}+jfW(Tf~e^A!1fqRgf=1U?R>|kqrNTPlOK?zWO-&*$;s7#wzJ5VCJB}vWk%AJ zLJ7%zINMy~;o0fOKd=C1Sqo9>i!owFCr!W$U{8r=>%cWOuR|=vLYirLjEe2;`ka69 zTq~2v#`VyQ6`rTO_1)XyHq!t{TOyP3DG|s08b1Q0=_PKw5SA`LGGLUGT1UZK*>Z`v znqv%l$0nIuYsnc$d~B2Y-X|s+>~3^Nz3$a>$2%OH0!fDNBoc8;1EMJqh#~7Yx24xWCKtB zUC!KWP=tb%L>9Q08&t3KCb{Y}b^?NnSq_`Fn`6UV!4PJT?k5$CEb*nCz||VqI>TSq zS<`MY=yI3XZF}zF7vUi`@jOqZ?(gX4f~Qk3ITtKx{onismf_hNFs7YKOezPJrVI}Wv5+&ntFmc9YbFy^YgvW%>;5e7BV%9tSi#gk6f&vV> zB_%%bhjyE_*^SF#o9m#^-Rfi+dGa6H%|TJ@Tnji^6gq>rsns9^UsQ@HGjG8x$T^8F zr5k+^^&&s-)E#%E4<00}!NgI1vie??KKirDjxGePPS24p9BuYLtTu-h&oj0bHeD#e zjvhvt*JV^;=~ejb422D zGPz-y*1B^AUG)i@88O@GoE^aX7I1BwmOFw98?>E;FdzR}0)0?a4DGaA2CW{81`KUf zLJ}QqnyioDpuOWbTB4V7C$K{T?WYdGUmFe6@vCB<$p2lc>jwAzI@ylG0bj5MjyS?I z`0>6)y&s>nb!<|Xm~#N&sv8t&5A?VJYJ?TwrL#ToL*r~4o&OiAJ_CsAi>!YA#mrXAEYH;!`9uwfw>-#%fD*n&j$ zp+ZiE?#q{*A*g^URka%rpNDEeUe(CWa^2wp!wpJax!(QIDswuf8dpPjfoav9@FYx3 zw?z~d(-=WEtts{`n03)>TPiZ;5Xz&X$oY%SQ32~!i`jURRSH&|&TLW|p(%M?35j~W zB^hWmS(D?;r2F_3(xA;;58^n@8QQwi6b3~`MR*JvP>L?MD`%jO*aIC{Oc$T3lxPTA zKBqZP4~2`o0WlTLVP~3#DLHZBVaOmfJqBr*dFHC1xWXQw9ziy-l@R7+$S#-5JFVdo zGDbE2p%qX0DT~NQS4R1Qubevu>v;M@B&@x$xo~gtC@t77CcpE~MUe98)H#YJ4oc!a;Ae#7rU@P5*B1}4cS{~_eukdZpIJHEhioud zOuJW8K+n5>;a1?Sy5kuVty52^m?Ws_8%oT~Xz=0$o&_8{YlS$v$l%T>? zZ#%ho!Nu2*zeB`hNh$hC%j4lZ8UiuMLHhXM@iPZ6kMoR1Q|Yft&}h^>?G7iT;#2e% zFm?a)R&z)c0;R3C8Z89BWeKwZ17MRGr?&1hKSvCTNeYhXsftgfvP>(O-(U=8$v{gS z0oRAzL~;(8n>~aoqLcKd!nJS1tRFyX@6UTfO-o?MoFUbE@g5{tLk}S-fZaO*t={f2 zgxWj5Li)pX$=t}VAeyIc{J#$TD$VX=*n`iZcR;`MSE0wCOn|oe%Xw`4&oINWNQxsI z6;Vk6g!HWObKsCG3gICP#8Vtm!oaU6j#Nnm0gY-(Nmd)dR%ky-qGJK5OfN$YuLPx= zgagZA7gs=RzISp`JQ1hX&ZKop%xaU`YG5{5{rGygDkTboxn~sWhnuhj3>=6+1QH@S zlM@WxugaMj-BSnoa?Y^E(qZ-TGdIN@rWxS$gONlLx`)&YHJglPF;g#SH^Gi2K?1a~ zTqXa$y9n&;&hG6k&e=A?i==ciU)p?tw&pPwq)l)7oWv zo~y(cJtE8Ef~dzC>MDhsJ8#>`NPnbCSU%1KAIi6Uhh#F4HB*4BuGEcV#~#0GAbNMl)On5tn$uNmUrd-k z^j(YR3uQeBQ-w-@penLaf=~LjuPQ~+nbFObx`P~u8FL*Vt{_*bo^xhrc zh~99{8dJHxK%$~;dsfXJ+-M4n+YqyS4kL5c&sZ75tOv4 zX}39kL*0$f?VWc5x7)1VEA>!jTwh<0K1vycmWaP6E%kZPz)q^m|#ef_~Rk7upbV6pD-zrCN) zGR5DI9`JL~%$h<6iRnl75HVJsXQoL;EiTewO(I2*fC4tq+<0axqS%Eq69BN=A67UZ z`g7_5dkIz1!`o+szNsY+RaToXymmC_OWzic0q|Le*xRJmrv}#UhxpFDJ!4G8vtF@ZT1pFv)dDH1*P8aKbi1Nw&1e zY&sPC(-Cy?J&vJGoL4krUxN0oUeATwZX_$VHj?y5fZN9Vd&3(GSV)v~m*}?XM<|&< zU_DrA{oEZe{LB)XXnsdRZ=>Iaye&Bubk>Bu1U`C{{#G9Y86j-ZR*sXXKiWZ2oJ~6?qIxH2eXkIEl$cLzk%rrxmoG|m(;c5e{xv3&DrRlvkiKuj9C@wp zRA(hz>yDobD)bL`GcT=IU555=-Q{VNN&fc^B(;VJ$CH|BwU}*bk&9r*eEy4NMGfG{ zII)!?L%LAod*3(Du&D!;u3+aP74qh4XrjfjQ08h%zy>R#>a_%6r|4O&hs>MZAFi8K zHXM4Ty{8HgW)e-IXCCJDi5lj%S@sC;|E%-cWf7P7z+evlZB0js96u=i;K(-Q#_5_6 zMF@Q@fD1B|s9#h`v2(3gL#srO-vK=@aMRZS(XW-HpTO{|gE@=1*gLJS@<((7o3J9M z$igI|b6G0h?e1zOv`<_9=RjO6h<$f4xw z{~%17Ss29=U50w@lJy1DN=%ql9u`b~LNjSPoS8)8iA1o=CIw4|QI4u+&fg+tfO)fvP0OhU>fzaLl)V ztQw|oIA!|_$l`GoLd75zn0Eu zI;zj@?KD#zOckdhsOox?x1~7GD{wiK@nqD+MT1!Z9ANzzR*);zR>aT*TX?3Jx8X0* zen&~7hg;ONe0naL_0qOmu|M^P)86mZVx5@rkgqkD*Sqm9oTktGs+f5dJ*EevMV4nm!+Tlu5K_X-t3S+knlm7i2s%4YS!e@jin^JtNUtG1GS+Qx4a8Bqb5 zn@H@iuD|>J@K|u4@5*9Z;*drciar{|AFPyYgAKXOd0$UR;cs)lv$pY5W3E`5?iaDN zqu=Y+t1D6!m%}5cx3OTkZ$lCbB{FA2qoLo+IgfMx@_!r~FI!%t|3QY82hbzuC~+~? oLa{wgo7<^@HBQ@1D64J3iDaRNEpVW2?%wVlLEEdpKlbzg0*hTlM*si- literal 0 HcmV?d00001 diff --git a/docs/performance.md b/docs/performance.md new file mode 100644 index 000000000..77fa625a6 --- /dev/null +++ b/docs/performance.md @@ -0,0 +1,87 @@ +# Performance Test + +Workflow-core version 3.7.0 was put under test to evaluate its performance. The setup used was single node with the default MemoryPersistenceProvider persistence provider. + +## Methodology + +- Test Environment - Test were run on following two environments one after the other to see how workflow-core performance with a lower vs higher hardware configuration. + - Lower configuration + - Cores: 8 vCPU ([Standard_D8s_v3](https://learn.microsoft.com/azure/virtual-machines/dv3-dsv3-series)) + - RAM: 32 GB + - OS: Linux Ubuntu 20.04 + - dotNet 6 + - Higher configuration + - Cores: 32 vCPU ([Standard_D32as_v4](https://learn.microsoft.com/azure/virtual-machines/dav4-dasv4-series)) + - RAM: 128 GB + - OS: Linux Ubuntu 20.04 + - dotNet 6 +- Test Workflow: Workflow consist of 3 basic steps. These 3 simple steps were chosen to test the performance of the workflow engine with minimal yet sufficient complexity and to avoid any external dependencies. + - Step1 : Generate a [random number](https://learn.microsoft.com/dotnet/api/system.random?view=net-6.0) between 1 to 10 and print it on standard output. + - Step2 : [Conditional step](https://github.com/danielgerlag/workflow-core/blob/master/docs/control-structures.md) + - Step 2.1: If value generate in step1 is > 5 then print it on standard output. + - Step 2.2: If value generate in step1 is <= 5 then print it on standard output. + - Step3: Prints a good bye message on standard output. +- Test tools: + - [NBomber](https://nbomber.com/docs/getting-started/overview/) was used as performance testing framework with C# console app as base. + +- Test scenarios: + - Each type of test run executed for 20 minutes. + - NBomber Load Simulation of type [KeepConstant](https://nbomber.com/docs/using-nbomber/basic-api/load-simulation#keep-constant) copies was used. This type of simulation keep a constant amount of Scenario copies(instances) for a specific period. + - Concurrent copies [1,2,3,4,5,6,7,8,10,12,14,16,32,64,128,256,512,1024] were tested. + - For example if we take Concurrent copies=4 and Duration=20 minutes this means that NBomber will ensure that we have 4 instance of Test Workflow running in parallel for 20 minutes. + +## Results + +- Workflow per seconds - Below tables shows how many workflows we are able to execute per second on two different environment with increasing number of concurrent copies. + +| **Concurrent Copies** | **8 vCPU** | **32 vCPU** | +| :-------------------: | :--------: | :---------: | +| **1** | 300.6 | 504.7 | +| **2** | 310.3 | 513.1 | +| **3** | 309.6 | 519.3 | +| **4** | 314.7 | 521.3 | +| **5** | 312.4 | 519.0 | +| **6** | 314.7 | 517.7 | +| **7** | 318.9 | 516.7 | +| **8** | 318.4 | 517.5 | +| **10** | 322.6 | 517.1 | +| **12** | 319.7 | 517.6 | +| **14** | 322.4 | 518.1 | +| **16** | 327.0 | 515.5 | +| **32** | 327.7 | 515.8 | +| **64** | 330.7 | 523.7 | +| **128** | 332.8 | 526.9 | +| **256** | 332.8 | 529.1 | +| **512** | 332.8 | 529.1 | +| **1024** | 341.3 | 529.1 | + +![Workflows Per Second](./images/performance-test-workflows-per-second.png) + +- Latency - Shows Mean, P99 and P50 latency in milliseconds on two different environment with increasing number of concurrent copies. + +| **Concurrent Copies** | **Mean 8 vCPU** | **Mean 32 vCPU** | **P.99 8 vCPU** | **P.99 32 vCPU** | **P.50 8 vCPU** | **P.50 32 vCPU** | +| :-------------------: | :-------------: | :--------------: | :-------------: | :--------------: | :-------------: | :--------------: | +| **1** | 3.32 | 1.98 | 12.67 | 2.49 | 3.13 | 1.85 | +| **2** | 6.43 | 3.89 | 19.96 | 5.67 | 6.17 | 3.65 | +| **3** | 9.67 | 5.77 | 24.96 | 8.2 | 9.14 | 5.46 | +| **4** | 12.7 | 7.76 | 27.44 | 13.57 | 12.02 | 7.22 | +| **5** | 15.99 | 9.63 | 34.59 | 41.89 | 15.14 | 9.08 | +| **6** | 19.05 | 11.58 | 38.69 | 45.92 | 18.02 | 10.93 | +| **7** | 21.94 | 13.54 | 42.18 | 48.9 | 20.72 | 12.66 | +| **8** | 25.11 | 15.45 | 44.35 | 51.04 | 23.92 | 14.54 | +| **10** | 30.98 | 19.33 | 52.29 | 56.64 | 29.31 | 18.21 | +| **12** | 37.52 | 23.18 | 59.2 | 63.33 | 35.42 | 21.82 | +| **14** | 43.44 | 27.01 | 67.33 | 67.58 | 41.28 | 25.55 | +| **16** | 48.93 | 31.03 | 72.06 | 72.77 | 46.11 | 28.93 | +| **32** | 97.65 | 62.03 | 130.05 | 104.96 | 94.91 | 58.02 | +| **64** | 193.53 | 122.24 | 235.14 | 168.45 | 191.49 | 115.26 | +| **128** | 384.63 | 243.74 | 449.79 | 294.65 | 379.65 | 236.67 | +| **256** | 769.13 | 486.82 | 834.07 | 561.66 | 766.46 | 498.22 | +| **512** | 1538.29 | 968.02 | 1725.44 | 1052.67 | 1542.14 | 962.05 | +| **1024** | 2999.36 | 1935.32 | 3219.46 | 2072.57 | 3086.34 | 1935.36 | + +![Latency](./images/performance-test-workflows-latency.png) + +## References + +- [NBomber](https://nbomber.com/docs/getting-started/overview/) From 406f4869527d95d8f6ff1cdfd61befc53f038b93 Mon Sep 17 00:00:00 2001 From: Stuart McKenzie Date: Fri, 28 Apr 2023 11:30:47 +1000 Subject: [PATCH 456/462] remove nulls from constructors and dry up the service extensions --- .../ServiceCollectionExtensions.cs | 37 ++++++++++++------- .../Services/DynamoDbProvisioner.cs | 4 +- .../Services/DynamoLockProvider.cs | 4 +- .../Services/DynamoPersistenceProvider.cs | 4 +- .../Services/KinesisProvider.cs | 4 +- .../Services/KinesisStreamConsumer.cs | 4 +- .../Services/KinesisTracker.cs | 4 +- .../Services/SQSQueueProvider.cs | 4 +- .../DynamoPersistenceProviderFixture.cs | 5 ++- 9 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs index 171860524..5d62bf3dc 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs @@ -1,6 +1,7 @@ using System; using Amazon; using Amazon.DynamoDBv2; +using Amazon.Kinesis; using Amazon.Runtime; using Amazon.SQS; using Microsoft.Extensions.Logging; @@ -15,47 +16,55 @@ public static class ServiceCollectionExtensions { public static WorkflowOptions UseAwsSimpleQueueService(this WorkflowOptions options, AWSCredentials credentials, AmazonSQSConfig config, string queuesPrefix = "workflowcore") { - options.UseQueueProvider(sp => new SQSQueueProvider(credentials, config, null, sp.GetService(), queuesPrefix)); - return options; + var sqsClient = new AmazonSQSClient(credentials, config); + return UseAwsSimpleQueueServiceWithProvisionedClient(options, sqsClient, queuesPrefix); } public static WorkflowOptions UseAwsSimpleQueueServiceWithProvisionedClient(this WorkflowOptions options, AmazonSQSClient sqsClient, string queuesPrefix = "workflowcore") { - options.UseQueueProvider(sp => new SQSQueueProvider(null, null, sqsClient, sp.GetService(), queuesPrefix)); + options.UseQueueProvider(sp => new SQSQueueProvider(sqsClient, sp.GetService(), queuesPrefix)); return options; } public static WorkflowOptions UseAwsDynamoLocking(this WorkflowOptions options, AWSCredentials credentials, AmazonDynamoDBConfig config, string tableName) { - options.UseDistributedLockManager(sp => new DynamoLockProvider(credentials, config, null, tableName, sp.GetService(), sp.GetService())); - return options; + var dbClient = new AmazonDynamoDBClient(credentials, config); + return UseAwsDynamoLockingWithProvisionedClient(options, dbClient, tableName); } public static WorkflowOptions UseAwsDynamoLockingWithProvisionedClient (this WorkflowOptions options, AmazonDynamoDBClient dynamoClient, string tableName) { - options.UseDistributedLockManager(sp => new DynamoLockProvider(null, null, dynamoClient, tableName, sp.GetService(), sp.GetService())); + options.UseDistributedLockManager(sp => new DynamoLockProvider(dynamoClient, tableName, sp.GetService(), sp.GetService())); return options; } public static WorkflowOptions UseAwsDynamoPersistence(this WorkflowOptions options, AWSCredentials credentials, AmazonDynamoDBConfig config, string tablePrefix) { - options.Services.AddTransient(sp => new DynamoDbProvisioner(credentials, config, null, tablePrefix, sp.GetService())); - options.UsePersistence(sp => new DynamoPersistenceProvider(credentials, config, null, sp.GetService(), tablePrefix, sp.GetService())); - return options; + var dbClient = new AmazonDynamoDBClient(credentials, config); + return UseAwsDynamoPersistenceWithProvisionedClient(options, dbClient, tablePrefix); } public static WorkflowOptions UseAwsDynamoPersistenceWithProvisionedClient(this WorkflowOptions options, AmazonDynamoDBClient dynamoClient, string tablePrefix) { - options.Services.AddTransient(sp => new DynamoDbProvisioner(null, null, dynamoClient, tablePrefix, sp.GetService())); - options.UsePersistence(sp => new DynamoPersistenceProvider(null, null, dynamoClient, sp.GetService(), tablePrefix, sp.GetService())); + options.Services.AddTransient(sp => new DynamoDbProvisioner(dynamoClient, tablePrefix, sp.GetService())); + options.UsePersistence(sp => new DynamoPersistenceProvider(dynamoClient, sp.GetService(), tablePrefix, sp.GetService())); return options; } public static WorkflowOptions UseAwsKinesis(this WorkflowOptions options, AWSCredentials credentials, RegionEndpoint region, string appName, string streamName) { - options.Services.AddTransient(sp => new KinesisTracker(credentials, region, "workflowcore_kinesis", sp.GetService())); - options.Services.AddTransient(sp => new KinesisStreamConsumer(credentials, region, sp.GetService(), sp.GetService(), sp.GetService(), sp.GetService())); - options.UseEventHub(sp => new KinesisProvider(credentials, region, appName, streamName, sp.GetService(), sp.GetService())); + var kinesisClient = new AmazonKinesisClient(credentials, region); + var dynamoClient = new AmazonDynamoDBClient(credentials, region); + + return UseAwsKinesisWithProvisionedClients(options, kinesisClient, dynamoClient,appName, streamName); + + } + + public static WorkflowOptions UseAwsKinesisWithProvisionedClients(this WorkflowOptions options, AmazonKinesisClient kinesisClient, AmazonDynamoDBClient dynamoDbClient, string appName, string streamName) + { + options.Services.AddTransient(sp => new KinesisTracker(dynamoDbClient, "workflowcore_kinesis", sp.GetService())); + options.Services.AddTransient(sp => new KinesisStreamConsumer(kinesisClient, sp.GetService(), sp.GetService(), sp.GetService(), sp.GetService())); + options.UseEventHub(sp => new KinesisProvider(kinesisClient, appName, streamName, sp.GetService(), sp.GetService())); return options; } } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs index 6d3c712b7..887d11a7a 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoDbProvisioner.cs @@ -15,10 +15,10 @@ public class DynamoDbProvisioner : IDynamoDbProvisioner private readonly IAmazonDynamoDB _client; private readonly string _tablePrefix; - public DynamoDbProvisioner(AWSCredentials credentials, AmazonDynamoDBConfig config, AmazonDynamoDBClient dynamoDBClient, string tablePrefix, ILoggerFactory logFactory) + public DynamoDbProvisioner(AmazonDynamoDBClient dynamoDBClient, string tablePrefix, ILoggerFactory logFactory) { _logger = logFactory.CreateLogger(); - _client = dynamoDBClient ?? new AmazonDynamoDBClient(credentials, config); + _client = dynamoDBClient; _tablePrefix = tablePrefix; } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs index 32ebe488b..0863f1393 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoLockProvider.cs @@ -25,10 +25,10 @@ public class DynamoLockProvider : IDistributedLockProvider private readonly AutoResetEvent _mutex = new AutoResetEvent(true); private readonly IDateTimeProvider _dateTimeProvider; - public DynamoLockProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, AmazonDynamoDBClient dynamoDBClient, string tableName, ILoggerFactory logFactory, IDateTimeProvider dateTimeProvider) + public DynamoLockProvider(AmazonDynamoDBClient dynamoDBClient, string tableName, ILoggerFactory logFactory, IDateTimeProvider dateTimeProvider) { _logger = logFactory.CreateLogger(); - _client = dynamoDBClient ?? new AmazonDynamoDBClient(credentials, config); + _client = dynamoDBClient; _localLocks = new List(); _tableName = tableName; _nodeId = Guid.NewGuid().ToString(); diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs index 0c78c6048..01beaaabe 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/DynamoPersistenceProvider.cs @@ -26,10 +26,10 @@ public class DynamoPersistenceProvider : IPersistenceProvider public bool SupportsScheduledCommands => false; - public DynamoPersistenceProvider(AWSCredentials credentials, AmazonDynamoDBConfig config, AmazonDynamoDBClient dynamoDBClient, IDynamoDbProvisioner provisioner, string tablePrefix, ILoggerFactory logFactory) + public DynamoPersistenceProvider(AmazonDynamoDBClient dynamoDBClient, IDynamoDbProvisioner provisioner, string tablePrefix, ILoggerFactory logFactory) { _logger = logFactory.CreateLogger(); - _client = dynamoDBClient ?? new AmazonDynamoDBClient(credentials, config); + _client = dynamoDBClient; _tablePrefix = tablePrefix; _provisioner = provisioner; } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs index 99d43d94f..d8aa519bd 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisProvider.cs @@ -26,7 +26,7 @@ public class KinesisProvider : ILifeCycleEventHub private readonly int _defaultShardCount = 1; private bool _started = false; - public KinesisProvider(AWSCredentials credentials, RegionEndpoint region, string appName, string streamName, IKinesisStreamConsumer consumer, ILoggerFactory logFactory) + public KinesisProvider(AmazonKinesisClient kinesisClient, string appName, string streamName, IKinesisStreamConsumer consumer, ILoggerFactory logFactory) { _logger = logFactory.CreateLogger(GetType()); _appName = appName; @@ -34,7 +34,7 @@ public KinesisProvider(AWSCredentials credentials, RegionEndpoint region, string _consumer = consumer; _serializer = new JsonSerializer(); _serializer.TypeNameHandling = TypeNameHandling.All; - _client = new AmazonKinesisClient(credentials, region); + _client = kinesisClient; } public async Task PublishNotification(LifeCycleEvent evt) diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs index 799125a0d..5c89f7837 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisStreamConsumer.cs @@ -25,12 +25,12 @@ public class KinesisStreamConsumer : IKinesisStreamConsumer, IDisposable private ICollection _subscribers = new HashSet(); private readonly IDateTimeProvider _dateTimeProvider; - public KinesisStreamConsumer(AWSCredentials credentials, RegionEndpoint region, IKinesisTracker tracker, IDistributedLockProvider lockManager, ILoggerFactory logFactory, IDateTimeProvider dateTimeProvider) + public KinesisStreamConsumer(AmazonKinesisClient kinesisClient, IKinesisTracker tracker, IDistributedLockProvider lockManager, ILoggerFactory logFactory, IDateTimeProvider dateTimeProvider) { _logger = logFactory.CreateLogger(GetType()); _tracker = tracker; _lockManager = lockManager; - _client = new AmazonKinesisClient(credentials, region); + _client = kinesisClient; _processTask = new Task(Process); _processTask.Start(); _dateTimeProvider = dateTimeProvider; diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs index 9c5548420..d7c028c46 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/KinesisTracker.cs @@ -17,10 +17,10 @@ public class KinesisTracker : IKinesisTracker private readonly string _tableName; private bool _tableConfirmed = false; - public KinesisTracker(AWSCredentials credentials, RegionEndpoint region, string tableName, ILoggerFactory logFactory) + public KinesisTracker(AmazonDynamoDBClient client, string tableName, ILoggerFactory logFactory) { _logger = logFactory.CreateLogger(GetType()); - _client = new AmazonDynamoDBClient(credentials, region); + _client = client; _tableName = tableName; } diff --git a/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs b/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs index af2c40b20..c15fb02af 100644 --- a/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs +++ b/src/providers/WorkflowCore.Providers.AWS/Services/SQSQueueProvider.cs @@ -21,10 +21,10 @@ public class SQSQueueProvider : IQueueProvider public bool IsDequeueBlocking => true; - public SQSQueueProvider(AWSCredentials credentials, AmazonSQSConfig config, AmazonSQSClient sqsClient, ILoggerFactory logFactory, string queuesPrefix) + public SQSQueueProvider(AmazonSQSClient sqsClient, ILoggerFactory logFactory, string queuesPrefix) { _logger = logFactory.CreateLogger(); - _client = sqsClient ?? new AmazonSQSClient(credentials, config); + _client = sqsClient; _queuesPrefix = queuesPrefix; } diff --git a/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs b/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs index 6ec1c13b6..7d215f4ee 100644 --- a/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs +++ b/test/WorkflowCore.Tests.DynamoDB/DynamoPersistenceProviderFixture.cs @@ -26,8 +26,9 @@ protected override IPersistenceProvider Subject if (_subject == null) { var cfg = new AmazonDynamoDBConfig { ServiceURL = DynamoDbDockerSetup.ConnectionString }; - var provisioner = new DynamoDbProvisioner(DynamoDbDockerSetup.Credentials, cfg, null, "unittests", new LoggerFactory()); - var client = new DynamoPersistenceProvider(DynamoDbDockerSetup.Credentials, cfg, null, provisioner, "unittests", new LoggerFactory()); + var dbClient = new AmazonDynamoDBClient(DynamoDbDockerSetup.Credentials, cfg); + var provisioner = new DynamoDbProvisioner(dbClient, "unittests", new LoggerFactory()); + var client = new DynamoPersistenceProvider(dbClient, provisioner, "unittests", new LoggerFactory()); client.EnsureStoreExists(); _subject = client; } From e1829f8174d78c03852ac16281b9b1c084ef7347 Mon Sep 17 00:00:00 2001 From: Stuart McKenzie Date: Fri, 28 Apr 2023 11:40:23 +1000 Subject: [PATCH 457/462] better patterns in the Service Collection Extensions (use the extension methods as intended) --- .../ServiceCollectionExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs index 5d62bf3dc..c3c545f80 100644 --- a/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs +++ b/src/providers/WorkflowCore.Providers.AWS/ServiceCollectionExtensions.cs @@ -17,7 +17,7 @@ public static class ServiceCollectionExtensions public static WorkflowOptions UseAwsSimpleQueueService(this WorkflowOptions options, AWSCredentials credentials, AmazonSQSConfig config, string queuesPrefix = "workflowcore") { var sqsClient = new AmazonSQSClient(credentials, config); - return UseAwsSimpleQueueServiceWithProvisionedClient(options, sqsClient, queuesPrefix); + return options.UseAwsSimpleQueueServiceWithProvisionedClient(sqsClient, queuesPrefix); } public static WorkflowOptions UseAwsSimpleQueueServiceWithProvisionedClient(this WorkflowOptions options, AmazonSQSClient sqsClient, string queuesPrefix = "workflowcore") @@ -29,7 +29,7 @@ public static WorkflowOptions UseAwsSimpleQueueServiceWithProvisionedClient(this public static WorkflowOptions UseAwsDynamoLocking(this WorkflowOptions options, AWSCredentials credentials, AmazonDynamoDBConfig config, string tableName) { var dbClient = new AmazonDynamoDBClient(credentials, config); - return UseAwsDynamoLockingWithProvisionedClient(options, dbClient, tableName); + return options.UseAwsDynamoLockingWithProvisionedClient(dbClient, tableName); } public static WorkflowOptions UseAwsDynamoLockingWithProvisionedClient (this WorkflowOptions options, AmazonDynamoDBClient dynamoClient, string tableName) @@ -41,7 +41,7 @@ public static WorkflowOptions UseAwsDynamoLockingWithProvisionedClient (this Wor public static WorkflowOptions UseAwsDynamoPersistence(this WorkflowOptions options, AWSCredentials credentials, AmazonDynamoDBConfig config, string tablePrefix) { var dbClient = new AmazonDynamoDBClient(credentials, config); - return UseAwsDynamoPersistenceWithProvisionedClient(options, dbClient, tablePrefix); + return options.UseAwsDynamoPersistenceWithProvisionedClient(dbClient, tablePrefix); } public static WorkflowOptions UseAwsDynamoPersistenceWithProvisionedClient(this WorkflowOptions options, AmazonDynamoDBClient dynamoClient, string tablePrefix) @@ -56,7 +56,7 @@ public static WorkflowOptions UseAwsKinesis(this WorkflowOptions options, AWSCre var kinesisClient = new AmazonKinesisClient(credentials, region); var dynamoClient = new AmazonDynamoDBClient(credentials, region); - return UseAwsKinesisWithProvisionedClients(options, kinesisClient, dynamoClient,appName, streamName); + return options.UseAwsKinesisWithProvisionedClients(kinesisClient, dynamoClient,appName, streamName); } From b7de0a1bff9668f14bdb629b3c39a82da79cb7f5 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 9 May 2023 08:25:04 -0700 Subject: [PATCH 458/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index a617157cb..19614367d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.8.2 - 3.8.2.0 - 3.8.2.0 + 3.8.3 + 3.8.3.0 + 3.8.3.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.8.2 + 3.8.3 From a423a7e72291e7ed257e2f25ec5dc2b48877ba08 Mon Sep 17 00:00:00 2001 From: Ben Edwards Date: Wed, 17 May 2023 12:42:16 +1000 Subject: [PATCH 459/462] Update EFCore to 7 Change all .net 6 package references to entity framework to 7.*. All tests pass :D --- .../WorkflowCore.Persistence.EntityFramework.csproj | 2 +- .../WorkflowCore.Persistence.MySQL.csproj | 4 ++-- .../WorkflowCore.Persistence.PostgreSQL.csproj | 8 ++++---- .../WorkflowCore.Persistence.SqlServer.csproj | 6 +++--- .../WorkflowCore.Persistence.Sqlite.csproj | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj index 6ab835915..b5fb079c9 100644 --- a/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj +++ b/src/providers/WorkflowCore.Persistence.EntityFramework/WorkflowCore.Persistence.EntityFramework.csproj @@ -22,7 +22,7 @@ - + diff --git a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj index aaf1f79b0..8b87ba216 100644 --- a/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj +++ b/src/providers/WorkflowCore.Persistence.MySQL/WorkflowCore.Persistence.MySQL.csproj @@ -35,11 +35,11 @@ - + all runtime; build; native; contentfiles; analyzers - + diff --git a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj index e321a340a..583c706f0 100644 --- a/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj +++ b/src/providers/WorkflowCore.Persistence.PostgreSQL/WorkflowCore.Persistence.PostgreSQL.csproj @@ -23,12 +23,12 @@ - - - + + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj index be099421a..b6db766bd 100644 --- a/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj +++ b/src/providers/WorkflowCore.Persistence.SqlServer/WorkflowCore.Persistence.SqlServer.csproj @@ -24,11 +24,11 @@ - - + + All - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj index 0bd622bde..88b9ceec9 100644 --- a/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj +++ b/src/providers/WorkflowCore.Persistence.Sqlite/WorkflowCore.Persistence.Sqlite.csproj @@ -24,7 +24,7 @@ - + From 12be88701780e1f1b2d5b5b5d98e7ce4df3e9913 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 13 Jun 2023 07:32:25 -0700 Subject: [PATCH 460/462] Update Directory.Build.props --- src/Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 19614367d..5d86bd3a5 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.8.3 - 3.8.3.0 - 3.8.3.0 + 3.9.0 + 3.9.0.0 + 3.9.0.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.8.3 + 3.9.0 From 11d461c6bd1f87fd820f22078a7f756414b637dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 23:11:13 +0000 Subject: [PATCH 461/462] Bump System.Linq.Dynamic.Core in /src/WorkflowCore.DSL Bumps [System.Linq.Dynamic.Core](https://github.com/zzzprojects/System.Linq.Dynamic.Core) from 1.2.13 to 1.3.0. - [Release notes](https://github.com/zzzprojects/System.Linq.Dynamic.Core/releases) - [Changelog](https://github.com/zzzprojects/System.Linq.Dynamic.Core/blob/master/CHANGELOG.md) - [Commits](https://github.com/zzzprojects/System.Linq.Dynamic.Core/compare/v1.2.13...v1.3.0) --- updated-dependencies: - dependency-name: System.Linq.Dynamic.Core dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/WorkflowCore.DSL/WorkflowCore.DSL.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj index b3ce61cef..94765a53e 100644 --- a/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj +++ b/src/WorkflowCore.DSL/WorkflowCore.DSL.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -11,7 +11,7 @@ - + From 23553226b846e71524ddadcb63963c815ae52fea Mon Sep 17 00:00:00 2001 From: James White Date: Sun, 6 Aug 2023 14:45:58 -0700 Subject: [PATCH 462/462] Fixed usage of WorkflowStep.Name when it could be null in WorkflowActivity. --- src/Directory.Build.props | 8 ++++---- src/WorkflowCore/Services/WorkflowActivity.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 5d86bd3a5..b86b7e833 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,10 +4,10 @@ https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md git https://github.com/danielgerlag/workflow-core.git - 3.9.0 - 3.9.0.0 - 3.9.0.0 + 3.9.1 + 3.9.1.0 + 3.9.1.0 https://github.com/danielgerlag/workflow-core/raw/master/src/logo.png - 3.9.0 + 3.9.1 diff --git a/src/WorkflowCore/Services/WorkflowActivity.cs b/src/WorkflowCore/Services/WorkflowActivity.cs index b580f98b6..b4599d367 100644 --- a/src/WorkflowCore/Services/WorkflowActivity.cs +++ b/src/WorkflowCore/Services/WorkflowActivity.cs @@ -59,7 +59,7 @@ internal static void Enrich(WorkflowStep workflowStep) activity.DisplayName += $" step {stepName}"; activity.SetTag("workflow.step.id", workflowStep.Id); - activity.SetTag("workflow.step.name", workflowStep.Name); + activity.SetTag("workflow.step.name", stepName); activity.SetTag("workflow.step.type", workflowStep.BodyType.Name); } }