{"id":69586,"date":"2017-01-12T17:44:42","date_gmt":"2017-01-12T17:44:42","guid":{"rendered":"https:\/\/www.simple-talk.com\/?p=69586"},"modified":"2026-03-06T13:28:17","modified_gmt":"2026-03-06T13:28:17","slug":"using-awss-simple-workflow-service-swf-c","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/using-awss-simple-workflow-service-swf-c\/","title":{"rendered":"AWS Simple Workflow Service (SWF) with C#: Developer Guide"},"content":{"rendered":"\n<p><a href=\"https:\/\/aws.amazon.com\/swf\/\" target=\"_blank\" rel=\"noopener\">AWS Simple Workflow Service (SWF)<\/a>, provided by <a href=\"https:\/\/aws.amazon.com\/\" target=\"_blank\" rel=\"noopener\">Amazon Web Services (AWS)<\/a>, is a managed workflow orchestration service that coordinates distributed application components using a message-passing model. Your C# code creates and responds to two types of messages: decision tasks (which determine what to do next) and activity tasks (which perform the actual work). SWF manages the underlying queues, tracks workflow state, and handles retries &#8211; your code never directly executes within SWF; it only processes messages.<\/p>\n\n\n\n<p>This guide walks through building an SWF solution in C# using the AWS SDK for .NET, from configuring access credentials to implementing a complete workflow with decision and activity handlers.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"h-swf-for-beginners\">SWF for Beginners<\/h1>\n\n\n\n<p>Developers who are new to AWS may feel intimidated at the prospect of implementing a SWF solution until they have gained some familiarity with Amazon Web Services and prototyped the Simple Workflow Service. This article will help with this, but breezes past general AWS concerns because there are plenty of resources that exist, such as, <a href=\"https:\/\/www.pluralsight.com\/\">PLURALSIGHT<\/a> and <a href=\"https:\/\/www.safaribooksonline.com\/\">Safari Books Online<\/a>, for learning more. We can\u2019t entirely avoid the preliminaries so, in this section, we\u2019ll explain a bit about SWF.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-swf-is-conceptually-simple\">SWF is (Conceptually) Simple<\/h2>\n\n\n\n<p>You need only jump over two conceptual hurdles before you can successfully code a workflow solution. First, SWF only processes messages; it does not execute any custom code. Second, custom code creates and responds to SWF messages. By combining these two ideas, you will see that a workflow solution is all about managing messages. This is easily illustrated as follows.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"471\" height=\"511\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-3.jpeg\" alt=\"\" class=\"wp-image-69587\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>In SWF parlance, <em>decision activity<\/em> types handle the \u201cWhat to Do\u201d chores while <em>activity<\/em> types account for the requested work. Custom code creates and responds to these two types. The SWF service for its part juggles and delivers these two activity types.<\/p>\n\n\n\n<p>Note: Readers wishing to learn more about the workflow\u2019s lifecycle may find the AWS\u2019 discussion an excellent place to begin their studies.<\/p>\n\n\n\n<p>If you\u2019ve worked with Microsoft MSMQ, you will find this familiar, barring a few abstractions. The most obvious abstraction is that SWF fully manages and hides the underlying queue instance. Another abstraction is the way that <em>Activity<\/em> types wrap text-based messages. There will be unfamiliar concepts too, such as the fact that there is a <em>Decision Activity<\/em> type that decides when and what to enqueue and dequeue. MSMQ has no equivalent feature.<\/p>\n\n\n\n<p>There is one significant difference between SWF and MSMQ: SWF neither directly nor indirectly executes custom code. It also does not reference, or house, any custom components or DLLs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-getting-started\">Getting Started<\/h2>\n\n\n\n<p>SWF solutions are made easier to code and debug by using the access credential feature of an AWS account. Access credentials are not the same as the username and password credentials for logging into AWS. They are exposed via the <a href=\"https:\/\/aws.amazon.com\/iam\/\">Identity and Access Management<\/a> (IAM) page.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"900\" height=\"673\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-32.png\" alt=\"\" class=\"wp-image-69588\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Access credentials expose AWS resources to developers via command line and integrated development environment (IDE) tools.<\/p>\n\n\n\n<p>The next step towards writing SWF code with Microsoft Visual Studio 2015 is to download and install the <a href=\"https:\/\/aws.amazon.com\/sdk-for-net\/\">AWS SDK for .NET<\/a>.<\/p>\n\n\n\n<p>The SDK sports several helpful features. The first of these is the creation of a profile that can hold developer\u2019s access keys. This dramatically minimizes security hassles associated with debugging and deploying code to the cloud with Visual Studio. You can load credentials with the AWS Explorer as shown below.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"598\" height=\"261\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-33.png\" alt=\"\" class=\"wp-image-69589\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>You don\u2019t actually need to entitle a profile \u2018default\u2019: Some might prefer to use their account name. The advantage of a common name such as \u2018default\u2019 arises when sharing code. The SDK allows for designating a profile name within a project as shown below.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;?xml version=\"1.0\" encoding=\"utf-8\" ?&gt;\n&lt;configuration&gt;\n  &lt;appSettings&gt;\n    &lt;add key=\"AWSProfileName\" value=\"default\"\/&gt;\n    &lt;add key=\"AWSRegion\" value=\"us-west-2\" \/&gt;\n  &lt;\/appSettings&gt;\n&lt;\/configuration&gt;\n<\/pre>\n\n\n\n<p>Developer-specific profile names would require that each developer would either need to update the \u2018AWSProfileName\u2019 property or create shared access keys. Few developers would find either option palatable, I suspect.<\/p>\n\n\n\n<p>You get a lot of flexibility by being able to specify an AWS region, but doing so is not trivial. All this article\u2019s efforts occur within \u2018us-west-2\u2019. If you are working purely in code, it is almost impossible to specify the wrong region, because you have to make it explicit. But when you are working within the AWS user-interface, it\u2019s a detail that is easily missed, as you will see from the toolbar snippet for our \u2018<strong>stdemodevuser<\/strong>\u2019 user.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"415\" height=\"205\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-34.png\" alt=\"\" class=\"wp-image-69590\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Developers who are unfamiliar with AWS\u2019 <a href=\"http:\/\/docs.aws.amazon.com\/general\/latest\/gr\/rande.html\">region<\/a> definitions might understandably not know that \u2018us-west-2\u2019 equates to the \u2018US West (Oregon)\u2019 selection.<\/p>\n\n\n\n<p>Before tackling our demonstration solution, readers may find it helpful and informative to create and execute the <em>Aws Image Process Workflow Sample<\/em> project provided with the SDK. Create it by selecting the template as shown below. As we\u2019ve already discussed, you will need some valid access keys before you start.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"946\" height=\"513\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-35.png\" alt=\"\" class=\"wp-image-69591\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>If you run the \u2018<em>Aws Image Process Workflow Sample\u2019<\/em> project with adequate permissions, you will see the WPF form below. You don\u2019t need to click the \u2018Start Workflow execution\u2019 button though it is a fun exercise. (Don\u2019t forget to select an image and name an S3 Bucket before you do!)<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"939\" height=\"520\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-36.png\" alt=\"\" class=\"wp-image-69592\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Before leaving the <em>Aws Image Process Workflow Sample, <\/em>inspect the <em>HistoryIterator <\/em>class in the project. Our solution borrows this wrapper exposing the all import event history generated by workflow instances as displayed via the \u2018<em>My Workflow Executions\u2019<\/em> page of AWS\u2019 <em>Amazon Simple Workflow Service Dashboard. <\/em>The <em>Events<\/em> tab shown below resulted from putting the \u2018<em>Aws Image Process Workflow Sample\u2019 <\/em>through its paces.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"918\" height=\"583\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-37.png\" alt=\"\" class=\"wp-image-69593\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"note\">Note: You may incur costs, although they are nominal as of December 2016, running the Aws Image Process Workflow Sample project.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"h-building-an-swf-solution\">Building an SWF Solution<\/h1>\n\n\n\n<p>Ironically, the effort that is required to implement an SWF solution makes you wonder why the \u2018S\u2019 stands for \u2018Simple\u2019. Code and workflow properties must dovetail tightly to properly exploit SWF, and that can make things complicated. Before diving into the code we\u2019ll begin by creating several artefacts that the SWF service expects.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-swf-setup\">SWF Setup<\/h2>\n\n\n\n<p>Before you can run a workflow, you need three SWF artefacts: domain, workflow and activity. The domain acts a container for workflows and activities, along with related event history. Workflows and activities cannot jump domains. Therefore, our demonstration begins by creating the \u2018<strong>StDemoDomain<\/strong>\u2019 domain. We accomplish this via the <em>Amazon Simple Workflow Service Dashboard<\/em> and clicking the \u2018<em>Manage Domain\u2019<\/em> button which brings up another page that includes a \u2018<em>Register New\u2019<\/em> button.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"701\" height=\"428\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-38.png\" alt=\"\" class=\"wp-image-69594\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>By clicking the \u2018<em>Register\u2019<\/em> button, we add our solitary \u2018<strong>StDemoWorkflow<\/strong>\u2019 workflow. Multiple workflows may be added to a domain. For our purposes, one is sufficient.<\/p>\n\n\n\n<p>Once the domain exists, don\u2019t forget to select it in \u2018<em>Amazon Simple Workflow Service Dashboard\u2019,<\/em> as shown below, before registering any other items.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"732\" height=\"248\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-39.png\" alt=\"\" class=\"wp-image-69595\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Now we can add a workflow to the domain!<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"585\" height=\"566\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-40.png\" alt=\"\" class=\"wp-image-69596\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Before clicking \u2018<em>Register Workflow\u2019<\/em>, it pays to scan the properties. Most of them deal with runtime behavior and can be overridden in code. When experimenting with SWF, most of the above property values will work well enough for starting out.<\/p>\n\n\n\n<p>The last chore before writing code is to register activities for our demonstration. For simplicity\u2019s sake, we created a family of them with equally expressive names, \u2018<strong>DemoActivity1\u2019<\/strong>, \u2018<strong>DemoActivity2\u2019<\/strong>, \u2018<strong>DemoActivity3\u2019<\/strong> and \u2018<strong>DemoActivity4\u2019<\/strong>. Except for the \u2018<em>Activity Type Name\u2019<\/em> and \u2018<em>Description\u2019<\/em> all of them are similarly defined.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"658\" height=\"535\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-41.png\" alt=\"\" class=\"wp-image-69597\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Scan the properties before clicking \u2018<em>Register Activity\u2019<\/em> in the \u2018<em>Create Activity\u2019<\/em> wizard review page. Most of these deal with runtime behavior and can be overridden in code when necessary.<\/p>\n\n\n\n<p>One final note about registration: Any developers who wish to avoid these manual steps may want to find the facility to register any and all SWF item via code. The <em>AWS Image Process Workflow Sample<\/em> contains sample code for bypassing the chore of manual item registration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-big-picture\">The Big Picture<\/h2>\n\n\n\n<p>Our demonstration implements two workflows. They consist of patterns regularly encountered in production applications. Both occur with the recently registered \u2018<strong>StDemoWorkflow\u2019<\/strong>, \u2018<strong>DemoActivity1\u2019<\/strong>, \u2018<strong>DemoActivity2<\/strong>,\u2019 \u2018<strong>DemoActivity3\u2019<\/strong> and \u2018<strong>DemoActivity4\u2019<\/strong> SWF items.<\/p>\n\n\n\n<p>Before we delve into the details of our workflows, note that none of our registered items contain any information about the flow. We have not configured anything anywhere that might inform SWF that \u2018<strong>DemoActivity2\u2019<\/strong> follows \u2018<strong>DemoActivity1\u2019<\/strong>. Developers who are familiar with other workflow products, such as Microsoft BizTalk Server, may have already noticed this detail. As we explore in the demonstration implementation, SWF expects the developer to construct workflows via <em>decision activity<\/em> types<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-scenario-1\">Scenario 1<\/h3>\n\n\n\n<p>The first scenario places two parallel activities within a serial workflow. Our discussion of it shows how we can make a workflow via decision activity types.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"868\" height=\"271\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-4.jpeg\" alt=\"\" class=\"wp-image-69598\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-scenario-2\">Scenario 2<\/h3>\n\n\n\n<p>The second scenario retries an activity after an elapsed time period. This common pattern requires that we more aggressively exploit the SWF event history as we\u2019ve already mentioned in discussing the <em>Aws Image Process Workflow Sample<\/em> project.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"588\" height=\"193\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-5.jpeg\" alt=\"\" class=\"wp-image-69599\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>With preliminaries out of the way, let\u2019s explore the implementation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-demonstration-solution\">Demonstration Solution<\/h2>\n\n\n\n<p>The <strong><em>SwfDemo<\/em><\/strong> solution implements our two scenarios. Each executes within the same console application, allowing a developer to step through and debug each of them. Remember that <strong><em>SwfDemo<\/em><\/strong> just demonstrates a concept. For example, the code generating <em>decision<\/em> activities may reside on different servers or processes to improve throughput versus code processing the \u201cworker\u201d activities.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-solution-overview\">Solution Overview<\/h3>\n\n\n\n<p><strong><em>SwfDemo<\/em><\/strong> contains one project with the same name and several classes.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"261\" height=\"306\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-42.png\" alt=\"\" class=\"wp-image-69600\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong><em>ActivityManager<\/em><\/strong> \u2013 Once started, it continuously asks SWF for the next task to process via its <em>Poll<\/em> function, then executes business logic via <strong><em>ProcessTask<\/em><\/strong>, and reports back to SWF via <strong><em>CompleteTask<\/em><\/strong>.<\/li>\n<\/ul>\n<\/div>\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"338\" height=\"99\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-43.png\" alt=\"\" class=\"wp-image-69601\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong><em>DecisionActivityManager<\/em><\/strong> \u2013 Once started, it also asks SWF continuously for any decision tasks requiring the creation of a decision list. With one in hand the <strong><em>CreateDecisionList<\/em><\/strong> function finds the latest <strong><em>WorkDemoActivityState<\/em><\/strong> from which it creates a decision activity with one of three functions: <strong><em>CreateDecisionActivity<\/em><\/strong>, <strong><em>CreateTimerDecision<\/em><\/strong>, or <strong><em>CreateCompleteWorkflowDecision<\/em><\/strong>.<\/li>\n<\/ul>\n<\/div>\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"828\" height=\"167\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-44.png\" alt=\"\" class=\"wp-image-69602\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong><em>HistoryIterator<\/em><\/strong> \u2013 This reads the workflow instance\u2019s event log (see earlier discussion). Readers who are interested in learning more about it should review comments found in the <em>Aws Image Process Workflow Sample<\/em> project\u2019s implementation.<\/li>\n\n\n\n<li><strong><em>Program<\/em><\/strong> \u2013 Console application driver initiating both the <strong><em>ActivityManager<\/em><\/strong> and <strong><em>DecisionActivityManager<\/em><\/strong>, as well as, responding to user workflow requests.<\/li>\n\n\n\n<li><strong><em>WorkDemo<\/em><\/strong> \u2013 Simulates \u2018real work\u2019 that the <strong><em>ActivityManager<\/em><\/strong> executes.<\/li>\n\n\n\n<li><strong><em>WorkDemoActivityState<\/em><\/strong> \u2013 A collection of workflow properties that is passed between SWF artefacts. These define the instance as well as assist decision-making.<\/li>\n<\/ul>\n<\/div>\n\n\n<p>These displayed references constitute <strong><em>SwfDemo<\/em><\/strong>\u2019s minimum needs. Production applications will likely include business-specific references or other system components facilitating communications with business data stores, lambda functions, services, queues, etc.<\/p>\n\n\n\n<p>Note: SWF does not require Newtonsoft.Json; it just eases working with JSON for our purposes.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"791\" height=\"317\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-45.png\" alt=\"\" class=\"wp-image-69603\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>The rest of this section explores <strong><em>SwfDemo<\/em><\/strong> key classes. We begin with what is apparently the simplest, <strong><em>WorkDemoActivityState<\/em><\/strong>, and end with the most complex, <strong><em>DecisionActivityManager<\/em><\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-workdemoactivitystate\">WorkDemoActivityState<\/h3>\n\n\n\n<p>This unassuming class serves as the home of the information which workflow instances inspect, respond to and update. <strong><em>WorkDemoActivityState<\/em><\/strong> enables our workflow to remain stateless since it is passed between the different SWF items. Managing state via an information-rich object such as <strong><em>WorkDemoActivityState<\/em><\/strong> is optional, a subject we will revisit near the end of the article.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">    public class WorkDemoActivityState\n    {\n        public DateTime EventTimestamp { get; set; }\n        public Amazon.SimpleWorkflow.EventType EventType { get; set; }\n        public int ScenarioNumber { get; set; }\n        public List&lt;int&gt; WorkRequested { get; set; }\n        public List&lt;int&gt; WorkCompleted { get; set; }\n\n        public override string ToString()\n        {\n            return $\"ScenarioNumber {ScenarioNumber}: \" +\n                   $\"Work Requested: {string.Join(\"|\", WorkRequested.ToArray())} \" +\n                   $\"Work Completed: {string.Join(\"|\", WorkCompleted.ToArray())}\";\n        }\n    }\n<\/pre>\n\n\n\n<p><strong><em>ScenarioNumber<\/em><\/strong> exemplifies a typical workflow instance state property. It informs both <strong><em>ActivityManager<\/em><\/strong> and <strong><em>DecisionActivityManager<\/em><\/strong> which of the different scenarios the user requested. In a production application, this information might define business-specific information, such as customer account or inventory item number. On the other hand, <strong><em>EventType<\/em><\/strong>, an SWF runtime property, does not denote any business interest. Rather, it is helpful data for the <strong><em>DecisionActivityManager<\/em><\/strong> as we will shortly see.<\/p>\n\n\n\n<p><strong><em>WorkRequested<\/em><\/strong> and <strong><em>WorkCompleted<\/em><\/strong> indirectly record the different activities via their name\u2019s suffix values. For example, a 2 integer in <strong><em>WorkRequested<\/em><\/strong> implies that \u2018<strong>DemoActivity2\u2019<\/strong> has been requested. There is a risk in including such information in a workflow state manager class. Managing such details demands close attention as you\u2019ll soon learn.<\/p>\n\n\n\n<p>Despite an unassuming nature, workflow state management classes constitute a critical element of most SWF solution designs. For example, external business information properties will likely lead to the execution of different workflows, such as one updating customer accounts or another checking inventory stock levels. Likewise, custom code may read workflow internal processing properties to determine next steps.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-program\">Program<\/h3>\n\n\n\n<p>The <strong><em>main<\/em><\/strong> method serves two purposes. The first purpose is to kick off tasks for processing the two activity types. Although these tasks could reside on other servers and in different processes to improve scale, they\u2019re run together in our demonstration for simplicity. The loop that follows contains the second. It traps user input to initiate new workflow instances via <strong>ExecuteScenario<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">        public static void Main(string[] args)\n        {\n            Task.Run(() =&gt; { new DecisionActivityManager().Start(); });\n\n            Task.Run(() =&gt; { new ActivityManager().Start(); });\n\n            Console.WriteLine();\n\n            while (true)\n            {\n                Console.Write(\"\\tEnter scenario number 1 or 2: \");\n\n                var scenarioNumber = Console.ReadKey().KeyChar - 48;\n\n                Console.WriteLine();\n                Console.WriteLine($\"\\tExecuting Workflow for Scenario #{scenarioNumber}.\");\n\n                ExecuteScenario(\n                    new WorkDemoActivityState { ScenarioNumber = scenarioNumber });                \n            }\n        }\n\n<\/pre>\n\n\n\n<p>In order to get meaningful output, you need to inspect the <em>SWF Dashboard<\/em> when executing <em>Main<\/em>. We\u2019ll show this later in the article.<\/p>\n\n\n\n<p><strong>ExecuteScenario<\/strong> kicks off the user-requested workflow. It begins with constructing a <strong><em>WorkDemoActivityState<\/em><\/strong> object. It is then passed to an <strong><em>AmazonSimpleWorkflowClient<\/em><\/strong> instance that is configured to communicate with the \u2018USWest2\u2019 AWS endpoint. Although the selection of regions is unrestricted, our SWF items reside in \u2018USWest2\u2019 (Oregon) which forces its designation.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">        public static void ExecuteScenario(\n            WorkDemoActivityState workDemoActivityState)\n        {\n            Task.Run(() =&gt;\n            {\n                var startWorkflowExecutionRequest = new StartWorkflowExecutionRequest\n                    {\n                        Domain = \"StDemoDomain\", \n                        WorkflowId = Guid.NewGuid().ToString(),\n                        WorkflowType = new WorkflowType\n                        {\n                            Name = \"StDemoWorkflow\",\n                            Version = \"1.0\"\n                        },\n                        Input = \n                            JsonConvert.SerializeObject(workDemoActivityState),\n                        ExecutionStartToCloseTimeout = \"30\", \n                        TagList = new List&lt;string&gt;\n                            { $\"Scenario # {workDemoActivityState.ScenarioNumber}\" }\n                    };\n\n                using (var amazonSimpleWorkflowClient =\n                    new AmazonSimpleWorkflowClient(RegionEndpoint.USWest2))\n                {\n                    amazonSimpleWorkflowClient.StartWorkflowExecution(\n                        startWorkflowExecutionRequest);\n                }\n            });\n        }\n    } \n<\/pre>\n\n\n\n<p>Without wishing to bore readers by repeating information found in the documentation, there are a few mistakes that are easily made when configuring a workflow request. <strong><em>Domain<\/em><\/strong> and <strong><em>WorkflowType<\/em> <em>Name<\/em><\/strong> and <strong><em>WorkflowType<\/em><\/strong> <strong><em>Version<\/em><\/strong> must exactly match registered values; capitalization matters. <strong><em>WorkflowId<\/em><\/strong> does not have to be a GUID, but it must be unique among workflow instances. <strong><em>TagList<\/em><\/strong> is not required but provides informative information when looking at workflow information in the <em>Amazon Simple Workflow Service Dashboard<\/em>.<\/p>\n\n\n\n<p>Setting <strong><em>ExecutionStartToCloseTimeout<\/em><\/strong> to 30 seconds is not an entirely arbitrary choice. It is just enough time for our scenarios to complete. Deciding how long a workflow instance \u201clives\u201d becomes critical in production environments. While SWF cannot terminate any executing activity processing when a workflow expires, it will cease generating, receiving and reacting to any activity messages.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-activitymanager-and-workdemo\">ActivityManager and WorkDemo<\/h3>\n\n\n\n<p>Once triggered via the <em>Start<\/em> method, <strong><em>ActvityManager<\/em><\/strong> continuously asks SWF if there is an activity for it to ponder. If one exists, it first performs the \u201cbusiness\u201d function via <strong><em>WorkDemo<\/em><\/strong>\u2019s <strong><em>WasteTime<\/em><\/strong> method and reports back to the service.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">public class ActivityManager\n    {\n        public void Start()\n        {\n            while (true)\n            {\n                using (var swfClient = new AmazonSimpleWorkflowClient(\n                    RegionEndpoint.USWest2))\n                {\n                    var activityTask = Poll(swfClient);\n\n                    if (string.IsNullOrEmpty(activityTask?.TaskToken)) continue;\n\n                    var workDemoActivityState = JsonConvert\n                        .DeserializeObject&lt;WorkDemoActivityState&gt;(activityTask.Input);\n\n                    workDemoActivityState = ProcessTask(\n                        activityTask.ActivityType.Name, workDemoActivityState);\n\n                    CompleteTask(swfClient, activityTask.TaskToken, workDemoActivityState);\n                }\n\n                Thread.Sleep(100);\n            }\n        }\n\n        private static ActivityTask Poll(\n            AmazonSimpleWorkflowClient amazonSimpleWorkflowClient)\n        {\n            var pollForActivityTaskRequest = new PollForActivityTaskRequest\n            {\n                Domain = \"StDemoDomain\", \n                TaskList = new TaskList { Name = \"defaultTaskList\" }\n            };\n\n            var pollForActivityTaskResponse = amazonSimpleWorkflowClient\n                .PollForActivityTask(pollForActivityTaskRequest);\n\n            return pollForActivityTaskResponse.ActivityTask;\n        }\n<\/pre>\n\n\n\n<p>The unending <em>while<\/em> loop leverages the ubiquitous <strong><em>AmazonSimpleWorkflowClient<\/em><\/strong> to talk with SWF. The first chore it performs is ask SWF for an <strong><em>ActivityTask<\/em><\/strong> via the <strong><em>Poll<\/em><\/strong> method. Within <strong><em>Poll<\/em><\/strong> the request must include both <strong><em>Domain<\/em><\/strong> and <strong><em>TaskList<\/em><\/strong> properties. The loop ends with an ugly <strong><em>Thread.Sleep<\/em><\/strong><em>(100)<\/em> to avoid hogging the CPU: Although ugly, the task of dealing with such concerns may not disappear in a production solution<\/p>\n\n\n\n<p>While discussing the full impact of the <strong><em>TaskList<\/em><\/strong> goes beyond the scope of this article, there are a few points worth noting. First, juggling the <strong><em>TaskList<\/em><\/strong> is an advanced option. Second, our demonstration employs the \u2018<strong>defaultTaskList\u2019<\/strong> <em>Name<\/em> everywhere keeping us and SWF from getting confused. For example, <strong><em>TaskList<\/em><\/strong> impacts which activities SWF returns when querying it for decisions or activities.<\/p>\n\n\n\n<p>After patiently waiting, <strong><em>Pool <\/em><\/strong>eventually gets a response with an activity for processing. Within the response we extract and serialize the aforementioned <strong><em>WorkDemoActivityState<\/em><\/strong> instance containing our business and workflow information enabling us to call <strong><em>WasteTime<\/em><\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">        private static WorkDemoActivityState ProcessTask(\n            string activityTypeName, WorkDemoActivityState workDemoActivityState)\n        {\n            WorkDemo.WasteTime(\n                int.Parse(activityTypeName.Last().ToString()), \n                workDemoActivityState);\n\n            return workDemoActivityState;\n        }\n<\/pre>\n\n\n\n<p>When <strong><em>ProcessTask<\/em><\/strong> finishes the business critical processing, it reports back to SWF how things fared. Within the <strong><em>RespondActivityTaskCompleted<\/em><\/strong> call it is important to append the updated <strong><em>WorkDemoActivityState<\/em><\/strong> object. Otherwise, our actions will go unknown to subsequent activities.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">        private static void CompleteTask(\n            AmazonSimpleWorkflowClient amazonSimpleWorkflowClient,\n            string taskToken, WorkDemoActivityState workDemoActivityState)\n        {\n            var respondActivityTaskCompletedRequest = \n                new RespondActivityTaskCompletedRequest()\n                {\n                    Result = JsonConvert.SerializeObject(workDemoActivityState),\n                    TaskToken = taskToken\n                };\n\n            try\n            {\n                amazonSimpleWorkflowClient.RespondActivityTaskCompleted(\n                respondActivityTaskCompletedRequest);\n            }\n            catch (Exception ex)\n            {\n                Debug.WriteLine($\"{workDemoActivityState} task complete failed: {ex}\");\n            }\n        }\n    }\n<\/pre>\n\n\n\n<p><strong><em>WorkDemo<\/em><\/strong> does not perform any real work other than burn computer cycles and add the activity number to the <strong><em>WorkCompleted<\/em><\/strong> list. Including some delay is important when fleshing out SWF prototypes. Managing timeouts becomes an important consideration when consuming production resources.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">   public class WorkDemo\n    {        \n        public static void WasteTime(\n            int workItemId, WorkDemoActivityState workDemoActivityState)\n        {\n            workDemoActivityState.WorkCompleted.Add(workItemId);           \n            System.Threading.Thread.Sleep(1000);\n        }\n    }\n\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-decisionactivitymanager\">DecisionActivityManager<\/h3>\n\n\n\n<p>The success of most SWF solutions resides in a class like <strong><em>DecisionActivityManager<\/em><\/strong>. It starts off much like <strong><em>ActivityManager<\/em><\/strong> except now it looks for the next decision activity which SWF creates after it receives information after <strong><em>ActivityManager<\/em><\/strong> executed <strong><em>CompleteTask<\/em><\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">    public class DecisionActivityManager\n    {\n        public void Start()\n        {\n            while (true)\n            {\n                using (var swfClient = new AmazonSimpleWorkflowClient(RegionEndpoint.USWest2))\n                {\n                    var decisionTask = Poll(swfClient);\n\n                    if (!string.IsNullOrEmpty(decisionTask?.TaskToken))\n                    {\n                        var decisions = CreateDecisionList(decisionTask);\n\n                        CompleteDecisionTasks(swfClient, decisionTask.TaskToken, decisions);\n                    }\n                }\n\n                Thread.Sleep(100);\n            }\n        }\n\n        private static DecisionTask Poll(\n           AmazonSimpleWorkflowClient amazonSimpleWorkflowClient)\n        {\n            var pollForDecisionTaskRequest = new PollForDecisionTaskRequest()\n            {\n                Domain = \"StDemoDomain\",\n                TaskList = new TaskList\n                {\n                    Name = \"defaultTaskList\"\n                }\n            };\n\n            var pollForDecisionTaskResponse = amazonSimpleWorkflowClient\n                .PollForDecisionTask(pollForDecisionTaskRequest);\n\n            return pollForDecisionTaskResponse.DecisionTask;\n        }\n<\/pre>\n\n\n\n<p>The endless loop becomes more interesting when SWF provides a meaningful decision activity as determined by the existence of a non-null <strong><em>TaskToken<\/em><\/strong>. When that happens, create a list of decisions for return to SWF. This list embodies the undeclared workflow scenario flow.<\/p>\n\n\n\n<p>Building the decision list involves two tasks:<\/p>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li>Interpreting what has happened thus far in the workflow instance as recorded by SWF<\/li>\n\n\n\n<li>Deciding what activity or activities comes next based on that history.<\/li>\n<\/ol>\n<\/div>\n\n\n<p>There are many ways of building decision lists. The method that follows suits this particular job. It goes through the Event history, reconstructing the most recent <strong><em>WorkDemoActivityState<\/em><\/strong> instance with which we will choose the activities for inclusion in the task list. Whatever the tactics, responding to a workflow instance\u2019s Event history is required in any non-trivial SWF application.<\/p>\n\n\n\n<p>After acquiring the raw Event history via the <strong><em>HistoryIterator<\/em><\/strong> helper, we build a history of <strong><em>WorkDemoActivityState<\/em><\/strong> instances from which we cull the most recent. The job of constructing each <strong><em>WorkDemoActivityState<\/em><\/strong> begins with inspecting what SWF Workflow event it is associated with. With that insight we can appropriately read and update <strong><em>WorkDemoActivityState<\/em><\/strong> objects.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">        private static WorkDemoActivityState GetLastWorkDemoActivityState(\n            DecisionTask decisionTask)\n        {\n            var workDemoActivityStates =\n                new List&lt;WorkDemoActivityState&gt;();\n            var timerWorkDemoActivityStates =\n                new Dictionary&lt;string, WorkDemoActivityState&gt;();\n\n            using (var swfClient = new AmazonSimpleWorkflowClient(RegionEndpoint.USWest2))\n            {\n                var historyIterator = new HistoryIterator(\n                    swfClient, decisionTask, \"StDemoDomain\", \"defaultTaskList\");\n\n                foreach (var historyEvent in historyIterator)\n                {\n<\/pre>\n\n\n\n<p>The first event that we respond to occurs after initializing a workflow instance. Rehydrating the <strong><em>WorkDemoActivityState<\/em><\/strong> provides the business property <strong><em>ScenarioNumber<\/em><\/strong>; the non-business properties <strong><em>EventTimestamp<\/em><\/strong> and <strong><em>EventType<\/em><\/strong> are read directly from the history event instance. The <strong><em>WorkRequested<\/em><\/strong> and <strong><em>WorkCompleted<\/em><\/strong> lists are initialized for subsequent usage.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                    if (historyEvent.EventType == EventType.WorkflowExecutionStarted)\n                    {\n                        var workDemoActivityState = JsonConvert\n                            .DeserializeObject&lt;WorkDemoActivityState&gt;(\n                                historyEvent.WorkflowExecutionStartedEventAttributes.Input);\n\n                        workDemoActivityStates.Add(new WorkDemoActivityState\n                        {\n                            EventTimestamp = historyEvent.EventTimestamp,\n                            EventType = EventType.WorkflowExecutionStarted,\n                            ScenarioNumber = workDemoActivityState.ScenarioNumber,\n                            WorkRequested = new List&lt;int&gt;(),\n                            WorkCompleted = new List&lt;int&gt;()\n                        });\n                    }\n<\/pre>\n\n\n\n<p>The next event handler reflects how SWF works. The <strong><em>ActivityTaskCompleted<\/em><\/strong> history event is SWF\u2019s response after <strong><em>ActivityManager<\/em><\/strong> worked through the prior decision list. That necessitates SWF to query for the next decision list.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                    if (historyEvent.EventType == EventType.ActivityTaskCompleted)\n                    {\n                        var workDemoActivityState = JsonConvert\n                            .DeserializeObject&lt;WorkDemoActivityState&gt;(\n                                historyEvent.ActivityTaskCompletedEventAttributes.Result);\n\n                        workDemoActivityStates.Add(new WorkDemoActivityState\n                        {\n                            EventTimestamp = historyEvent.EventTimestamp,\n                            EventType = EventType.ActivityTaskCompleted,\n                            ScenarioNumber = workDemoActivityState.ScenarioNumber,\n                            WorkRequested = workDemoActivityState.WorkRequested,\n                            WorkCompleted = workDemoActivityState.WorkCompleted\n                        });\n                    }\n<\/pre>\n\n\n\n<p>Discriminating between the different homes for <strong><em>WorkDemoActivityState<\/em><\/strong> values in event history is easily overlooked. <strong><em>EventType<\/em><\/strong><em>.<\/em><strong><em>WorkflowExecutionStarted<\/em><\/strong> history stashes it in the appropriately named <strong><em>Input<\/em><\/strong> property. <strong><em>EventType<\/em><\/strong><em>.<\/em><strong><em>ActivityTaskCompleted<\/em><\/strong> history prefers the equally suitably named <strong><em>Result<\/em><\/strong> property.<\/p>\n\n\n\n<p>The last two conditions of the loop are not as obvious as first two. They attempt to summarize timer events in a meaningful fashion for the application. It enlists a temporary Dictionary with string keys and <strong><em>WorkDemoActivityState<\/em><\/strong> instances. The method dictionary, <strong><em>timerWorkDemoActivityStates<\/em><\/strong>, allows us to recall the <strong><em>WorkDemoActivityState<\/em><\/strong> associated with the request for a specific timer via <strong><em>TimerId<\/em><\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                    if (historyEvent.EventType == EventType.TimerStarted)\n                    {\n                        var workDemoActivityState = JsonConvert\n                            .DeserializeObject&lt;WorkDemoActivityState&gt;(\n                                historyEvent.TimerStartedEventAttributes.Control);\n\n                        timerWorkDemoActivityStates.Add(\n                            historyEvent.TimerStartedEventAttributes.TimerId,\n                            new WorkDemoActivityState\n                            {\n                                EventTimestamp = historyEvent.EventTimestamp,\n                                EventType = EventType.TimerStarted,\n                                ScenarioNumber = workDemoActivityState.ScenarioNumber,\n                                WorkRequested = workDemoActivityState.WorkRequested,\n                                WorkCompleted = workDemoActivityState.WorkCompleted\n                            });\n                    }\n\n                    if (historyEvent.EventType == EventType.TimerFired)\n                    {\n                        WorkDemoActivityState workDemoActivityState;\n                        if (timerWorkDemoActivityStates.TryGetValue(\n                            historyEvent.TimerFiredEventAttributes.TimerId, out workDemoActivityState))\n                        {\n                            workDemoActivityState.EventType = EventType.TimerFired;\n                            workDemoActivityStates.Add(workDemoActivityState);\n                        }\n                    }\n                }\n            }\n<\/pre>\n\n\n\n<p>It\u2019s understandable if these gyrations strike the reader as a kludge. But, they reflect how SWF manages activities. SWF includes minimal information <strong><em>EventType<\/em><\/strong><em>.<\/em><strong><em>TimerFired<\/em><\/strong>. If we want the <strong><em>WorkDemoActivityState<\/em><\/strong> associated with the timer, we need to inspect the linked <strong><em>EventType<\/em><\/strong><em>.<\/em><strong><em>TimerStarted<\/em><\/strong> history event.<\/p>\n\n\n\n<p>After working through all <strong><em>historyIterator<\/em><\/strong> items and constructing the related <strong><em>WorkDemoActivityState<\/em><\/strong> list, we are ready to return the prime object of our attention, the most current one.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">            return workDemoActivityStates.OrderByDescending(x =&gt; x.EventTimestamp).FirstOrDefault();\n        }\n<\/pre>\n\n\n\n<p><strong><em>DecisionActivityManager<\/em><\/strong>\u2019s <strong><em>CreateDecisionList<\/em><\/strong> constructs workflow instance decision lists based on the latest <strong><em>WorkDemoActivityState<\/em><\/strong>. It relies on a <em>switch<\/em> statement acting on <strong><em>ScenarioNumber<\/em><\/strong> to fabricate it.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">        private static List&lt;Decision&gt; CreateDecisionList(DecisionTask decisionTask)\n        {\n            var decisions = new List&lt;Decision&gt;();\n\n            var workDemoActivityState = GetLastWorkDemoActivityState(decisionTask);\n\n            if (workDemoActivityState == null) return decisions;\n\n            switch (workDemoActivityState.ScenarioNumber)\n            {\n<\/pre>\n\n\n\n<p>Scenario 1 begins by checking whether or not it has processed the last activity, \u2018<strong>DemoActivity4\u2019<\/strong>. It does so by looking for the activity name\u2019s last character in the <strong><em>WorkCompleted<\/em><\/strong> list. If so, it sends a message to SWF that the workflow has ended via <strong><em>CreateCompleteWorkflowDecision<\/em><\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                case 1:                  \n\n                    if (workDemoActivityState.WorkCompleted.Contains(4))\n                    {\n                        decisions.Add(CreateCompleteWorkflowDecision(workDemoActivityState));\n                        return decisions;\n                    }\n\n<\/pre>\n\n\n\n<p>Our next condition checks if \u2018<strong>DemoActivity1\u2019<\/strong> as been requested by looking for the activity name\u2019s last character in the <strong><em>WorkRequested<\/em><\/strong> list. If true, it sends a message to SWF that it needs to spin up an activity request with <strong><em>CreateDecisionActivity<\/em><\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                    if (!workDemoActivityState.WorkRequested.Contains(1))\n                    {\n                        workDemoActivityState.WorkRequested.Add(1);\n                        decisions.Add(CreateDecisionActivity(workDemoActivityState, 1));\n                        return decisions;\n                    } \n<\/pre>\n\n\n\n<p>The check for \u2018<strong>DemoActivity2\u2019<\/strong> behaves differently from the last one. When <strong><em>WorkRequested<\/em><\/strong> does not contain the expected 2 integer value, we call <strong><em>CreateDecisionActivity<\/em><\/strong> twice for \u2018<strong>DemoActivity2\u2019<\/strong> and \u2018<strong>DemoActivity3\u2019<\/strong> thus creating parallel activities.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                    if (!workDemoActivityState.WorkRequested.Contains(2))\n                    {\n                        workDemoActivityState.WorkRequested.Add(2);\n                        decisions.Add(CreateDecisionActivity(\n                            workDemoActivityState, 2));\n\n                        workDemoActivityState.WorkRequested.Add(3);\n                        decisions.Add(CreateDecisionActivity(\n                            workDemoActivityState, 3));\n\n                        return decisions;\n                    }\n<\/pre>\n\n\n\n<p>Attentive readers will no doubt have noticed that our application will not truly process \u2018<strong>DemoActivity2\u2019<\/strong> and \u2018<strong>DemoActivity3\u2019<\/strong> in parallel. Nonetheless, it is possible by adding an another .NET <em>Task<\/em> executing <strong><em>ActivityManager<\/em><\/strong><em>.<\/em><strong><em>Start<\/em><\/strong> in <em>Main<\/em>. In a production environment code akin to <strong><em>ActivityManager<\/em><\/strong><em>.<\/em><strong><em>Start<\/em><\/strong> will likely reside in other servers, tasks and processes to enhance scale.<\/p>\n\n\n\n<p>The first scenario concludes by checking for \u2018<strong>DemoActivity2\u2019<\/strong>. If found, it adds the last activity, \u2018DemoActivity4\u2019 in a fashion similar to that of the first activity.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                    if (workDemoActivityState.WorkRequested.Contains(2))\n                    {\n                        workDemoActivityState.WorkRequested.Add(4);\n                        decisions.Add(CreateDecisionActivity(workDemoActivityState, 4));\n                        return decisions;\n                    }                    \n\n                    break;\n<\/pre>\n\n\n\n<p>Decision-making mechanics for the second scenario differs from the first. In this simpler workflow the code just wants to know how many times \u2018<strong>DemoActivity1\u2019<\/strong> has been requested. Based on that count it can add a \u2018<strong>DemoActivity1\u2019<\/strong> as already seen; create a timer-driven decision activity type via <strong><em>CreateTimerDecision<\/em><\/strong>; or end the workflow.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">               case 2:\n\n                    var activityOneCount = workDemoActivityState\n                        .WorkRequested.Count(x =&gt; x.Equals(1));\n\n                    switch (activityOneCount)\n                    {\n                        case 0:\n\n                            workDemoActivityState.WorkRequested.Add(1);\n                            decisions.Add(CreateDecisionActivity(\n                                workDemoActivityState, 1));\n                            return decisions;\n<\/pre>\n\n\n\n<p>Unlike much of the other decision making logic within <strong><em>CreateDecisionList<\/em><\/strong>, we now check the <strong><em>EventType<\/em><\/strong>. This information helps us decide whether we need to add a first or second \u2018<strong>DemoActivity1\u2019<\/strong> activity request or create a decision timer.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                        case 1:\n\n                            if (workDemoActivityState.EventType == EventType.TimerFired)\n                            {\n                                workDemoActivityState.WorkRequested.Add(1);\n                                decisions.Add(CreateDecisionActivity(\n                                    workDemoActivityState, 1));\n                            }\n                            else\n                            {\n                                decisions.Add(CreateTimerDecision(\n                                    workDemoActivityState));\n                            }\n                            return decisions;\n<\/pre>\n\n\n\n<p>The <strong><em>DecisionActivityManager<\/em><\/strong> class review concludes with a few helpers. While much of it may now seem familiar, there a few details worth noting. First, <strong><em>ActivityId<\/em><\/strong> and <strong><em>TimerId<\/em><\/strong> must be unique for their activity types among all active workflow instances. When SWF encounters duplicate ids it assumes something went awry and reissues a request for a new decision task list. Second, note the different attribute classes and their property name referencing the serialized <strong><em>WorkDemoActivityState<\/em><\/strong>.<\/p>\n\n\n\n<p>Note: DateTime.Now.Ticks does not ensure uniqueness. While fine for demonstration purposes generating unique ids for production usage will require additional deliberation.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">        private static Decision CreateDecisionActivity(\n            WorkDemoActivityState workDemoActivityState, int activitySuffix)\n        {\n            var decision = new Decision\n            {\n                DecisionType = DecisionType.ScheduleActivityTask,\n                ScheduleActivityTaskDecisionAttributes =\n                    new ScheduleActivityTaskDecisionAttributes\n                    {\n                        ActivityType = new ActivityType\n                        {\n                            Name = $\"DemoActivity{activitySuffix}\",\n                            Version = \"1.0\"\n                        },\n<\/pre>\n\n\n\n<p>Appending <strong><em>activitySuffix<\/em><\/strong> to <em>Ticks<\/em> for the <strong><em>ActivityId<\/em><\/strong> prevents SWF from getting confused with duplicates when adding our parallel activities so quickly.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                        ActivityId = $\"{DateTime.Now.Ticks}_{activitySuffix}\",\n                        Input = JsonConvert.SerializeObject(workDemoActivityState)\n                    }\n            };\n\n            return decision;\n        }\n\n        private static Decision CreateTimerDecision(\n            WorkDemoActivityState workDemoActivityState)\n        {\n            var decision = new Decision\n            {\n                DecisionType = DecisionType.StartTimer,\n                StartTimerDecisionAttributes = new StartTimerDecisionAttributes\n                {\n<\/pre>\n\n\n\n<p>Timer decision activities need to know the number of seconds you expect it to wait before firing its completed event partner. Our instance sets <strong><em>StartToFireTimeout<\/em><\/strong> to 15 seconds. While our demonstration loads <strong><em>WorkDemoActivityState<\/em><\/strong> into <strong><em>Control<\/em><\/strong>, that is not required by SWF. We include it with plans for accessing the object in <strong><em>GetLastWorkDemoActivityState<\/em><\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                    StartToFireTimeout = \"15\",\n                    TimerId = DateTime.Now.Ticks.ToString(),\n                    Control = JsonConvert.SerializeObject(workDemoActivityState)\n                }\n            };\n\n            return decision;\n        }\n\n        private static Decision CreateCompleteWorkflowDecision(\n            WorkDemoActivityState workDemoActivityState)\n        {\n            var decision = new Decision\n            {\n                DecisionType = DecisionType.CompleteWorkflowExecution,\n                CompleteWorkflowExecutionDecisionAttributes =\n                    new CompleteWorkflowExecutionDecisionAttributes\n                    {\n<\/pre>\n\n\n\n<p><strong><em>Result<\/em><\/strong> is optional; but like the aforementioned <strong><em>TagList<\/em><\/strong>, it can be helpful when plumbing <em>Dashboard My Workflow Executions<\/em> for insights.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                        Result = workDemoActivityState.ToString()\n                    }\n            };\n\n            return decision;\n        }\n    }\n}\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-in-action\">In Action<\/h2>\n\n\n\n<p>Visiting the <em>Dashboard My Workflow Executions<\/em> page after running each scenario offers an excellent glimpse into SWF processing.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1075\" height=\"593\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-46.png\" alt=\"\" class=\"wp-image-69604\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p><em>My Workflow Executions<\/em> displays \u2018Open\u2019 or \u2018Closed\u2019 workflows during the filtered time period. The default is \u2018Open\u2019 and forgetting this detail when searching for a recently closed workflow may frustrate those new to SWF interface.<\/p>\n\n\n\n<p>Reviewing the <em>Activities<\/em> tab for the first scenario suggests that all the expecting activities, \u2018DemoActivity1\u2019 through \u2018DemoActivity4\u2019 executed. Closer inspection of the selected \u2018DemoActivity4\u2019 properties suggests something may not have gone entirely as expected though.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"731\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-47.png\" alt=\"\" class=\"wp-image-69605\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>The <strong><em>Result<\/em><\/strong> property, which equates to the workhorse <strong><em>WorkDemoActivityState<\/em><\/strong>, informs us that <strong><em>WorkCompleted<\/em><\/strong> differs from <strong><em>WorkRequested<\/em><\/strong>. According to the <strong><em>WorkCompleted<\/em><\/strong> list \u2018<strong>DemoActivity2\u2019<\/strong> was never executed despite the <em>Activities<\/em> tab. What gives?<\/p>\n\n\n\n<p>While SWF supports multiple threads and the like, it does not lock <strong><em>WorkDemoActivityState<\/em><\/strong> between activity operations. Unfortunately, our implementation writes to <strong><em>WorkCompleted<\/em><\/strong> in one task and reads it in another one. Fortunately, our flawed implementation never suffers the consequences. Avoiding shared state errors starts with the design of the SWF solution.<\/p>\n\n\n\n<p>Our second workflow executes without any obvious issues upon inspection of its <em>Activities<\/em>.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"999\" height=\"645\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-48.png\" alt=\"\" class=\"wp-image-69606\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-before-we-can-congratulate-ourselves-though-try-quickly-running-several-workflows-as-shown-below\">Before we can congratulate ourselves though, try quickly running several workflows as shown below.<\/h2>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"301\" height=\"219\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-49.png\" alt=\"\" class=\"wp-image-69607\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Returning to the <em>My Workflow Executions<\/em> page after a few minutes have elapsed hints depending on environment there may be hints of a new issue. As show below some workflow instances timed out.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"707\" height=\"321\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-50.png\" alt=\"\" class=\"wp-image-69608\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>We get the explanation when we add up the time that each workflow consumes, and comparing it to the time allowed when requested. Our <strong><em>StartWorkflowExecutionRequest<\/em>\u2019s<\/strong> <strong><em>ExecutionStartToCloseTimeout<\/em><\/strong> property value of 30 seconds isn\u2019t long enough for our implementation to work through too many simultaneous workflows.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-implementation-considerations\">Implementation Considerations<\/h2>\n\n\n\n<p>Architecting SWF-dependent applications abounds with design and configuration choices. And as our two scenarios\u2019 issues suggest it is not difficult to getting some wrong. In this section, we note a few thoughts to consider when building solutions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cost\">Cost<\/h3>\n\n\n\n<p>Although not a technical worry, the cost of any technology eventually influences any design decision. As of December 2016, though, it should not be a significant factor when considering SWF. With usage charges ranging from free to embarrassingly inexpensive, exploring, prototyping and testing incurs negligible infrastructure related costs. Most production applications find the cost of the resources employed by SWF more noteworthy than those billed for SWF.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-resources\">Resources<\/h3>\n\n\n\n<p>Although SWF minimizes infrastructure costs, it does not do the same for the resources it consumes. However, SWF allows you to segregate almost every implementation facet so it is almost impossible to design solutions without being aware of all the resources ultimately involved.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-state\">State<\/h3>\n\n\n\n<p>Two broad approaches exist for maintaining state in SWF. The first follows variants of the tack employed in this article\u2019s demonstration code. The alternative involves using an external data store. The first approach usually scales best but demands careful coding. The second also requires care as it introduces an independent resource into the mechanics of SWF.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-configuration\">Configuration<\/h3>\n\n\n\n<p>There are many configuration options to help deal with more advanced scenarios, but these are beyond the scope of this article. API support is moderately complete. It ranges from allowing the registration of SWF artefacts to altering runtime properties. This section only touches upon a few of them. I have found the <a href=\"http:\/\/docs.aws.amazon.com\/amazonswf\/latest\/apireference\/Welcome.html\">SDK<\/a> to be adequate when asking tricky questions.<\/p>\n\n\n\n<p><strong>Read also:<\/strong> <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/how-to-use-parameters-in-powershell\/\" target=\"_blank\" rel=\"noreferrer noopener\">PowerShell automation for AWS<\/a><\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-priority\">Priority<\/h4>\n\n\n\n<p>Our demonstration did not ask any priority questions. However, you can alter the way that SWF orders the polling requests by managing such properties as <strong><em>defaultTaskPriority<\/em><\/strong> and <strong><em>taskPriority<\/em><\/strong>, This is useful when, for example, if you wish the workflow to handle a workflow request generated from a user interface before a batch oriented request.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-tasklist\">TaskList<\/h4>\n\n\n\n<p>If there were a \u201cmost confusing\u201d title award in the SWF SDK, the <strong><em>taskList<\/em><\/strong> property gets my vote. It\u2019s overloaded between activity types and enables segregation by resource for processing. This allows developers to ensure, for example, that only certain servers handle specific activities.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-timeout\">Timeout<\/h4>\n\n\n\n<p>Aside from discovering that workflow instances can timeout, we barely scratched the surface of this subject. Exactly how time impacts workflows and activities depends on implementation details. Anticipate monitoring and tweaking them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-workflows\">Workflows<\/h3>\n\n\n\n<p>Our demonstration employed one registered workflow. Although it worked well enough, it was also quite na\u00efve. Production implementations regularly employ multiple workflows to simplify implementations, in terms of code and resources. They also can help minimize risks associated with workflow instances generating exceedingly large event histories.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"h-a-simpler-option\">A Simpler Option?<\/h1>\n\n\n\n<p>Many developers who encounter SWF for the first time are likely to ask the reasonable question, \u201cWhy so complex?\u201d The answer contains two parts. First, SWF\u2019s full name, Simple Workflow Service, means just that \u2013 simple. This complete service does not lend itself to a rapid application development. Second, it is a considerable task building any durable and reliable workflow-driven application, such as <em>SwfDemo, <\/em>whatever you use to build it.<\/p>\n\n\n\n<p>Late in 2016 AWS addressed such pressing needs with the introduction of the <a href=\"http:\/\/docs.aws.amazon.com\/step-functions\/latest\/dg\/welcome.html\">Step Functions<\/a> service. It facilitates the creation of state machines with a JSON driven, graphic designer. The below picture is an adaptation of one of our scenarios as a Step Function State Machine.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"288\" height=\"374\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-51.png\" alt=\"\" class=\"wp-image-69609\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>As with most things that sound too good to be true, there\u2019s a catch. Step Functions execute AWS <a href=\"http:\/\/docs.aws.amazon.com\/lambda\/latest\/dg\/welcome.html\">Lambda<\/a> functions, which come with restrictions. One of these is that the current C# Lambda release only supports the .NET Core runtime. Despite potential roadblocks, the Step Function service promises a more straightforward path for developers wishing to exploit durable workflows with as little hassle as possible.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"h-conclusion\">Conclusion<\/h1>\n\n\n\n<p>Workflows are simple until you need them to be durable and robust. The article\u2019s demonstration solution will hopefully guide readers through the bewildering early stages of implementing their first via SWF. Beyond implementation details two generalizations merit note. First, it is neither easy nor difficult with C# to leverage the AWS Simple Workflow Service. Second, carefully consider whether your solution requires a durable workflow capability. Adding workflow to an application isn\u2019t like sprinkling sugar on a cake: It will take time and effort. If an application really requires a grown-up workflow though, SWF stands outs as a promising option.<\/p>\n\n\n\n<p><strong>Read also:<\/strong> <a href=\"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/jwt-authentication-microservices-net\/\" target=\"_blank\" rel=\"noreferrer noopener\">JWT authentication for securing microservices<\/a><\/p>\n\n\n\n<section id=\"my-first-block-block_72117a1602c39b39d1bddf226d6c6a64\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Save 35% on Redgate&#8217;s .NET Developer Bundle<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            Fantastic value on our .NET development tools for performance optimization and debugging.                                    <\/div>\n            <\/div>\n                            <a href=\"https:\/\/www.red-gate.com\/products\/dotnet-developer-bundle\/\" class=\"btn btn--secondary btn--lg\">Learn more<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: Using AWS&#039;s Simple Workflow Service (SWF) with C#<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. What is the difference between AWS SWF and Step Functions?<\/h3>\n            <div class=\"faq-answer\">\n                <p>AWS Simple Workflow Service (SWF) uses a message-based programming model where your code polls for decision and activity tasks from queues. Step Functions uses a state-machine model where you define workflows as JSON-based Amazon States Language documents. Step Functions is generally simpler to implement and debug (it has a visual workflow console), while SWF gives you more programmatic control over the workflow logic. AWS recommends Step Functions for new implementations.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. How does SWF compare to Microsoft MSMQ?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Both SWF and MSMQ are message-based systems, but SWF is a fully managed cloud service that hides the queue infrastructure, tracks workflow state automatically, and adds the concept of decision tasks (workflow logic) on top of activity tasks (work units). MSMQ requires you to manage queue instances and implement your own workflow coordination. SWF also provides built-in retry logic and workflow history.<\/p>\n            <\/div>\n            <\/section>\n","protected":false},"excerpt":{"rendered":"<p>How to build workflow solutions using AWS Simple Workflow Service (SWF) with C# and the AWS SDK for .NET. Covers SWF concepts, decision and activity types, access credentials, and a working demo.&hellip;<\/p>\n","protected":false},"author":200451,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538,137092],"tags":[],"coauthors":[17935],"class_list":["post-69586","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","category-aws"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/69586","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/200451"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=69586"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/69586\/revisions"}],"predecessor-version":[{"id":109003,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/69586\/revisions\/109003"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=69586"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=69586"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=69586"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=69586"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}