Part 5 of Building Workflow Driven .NET Applications with Elsa 2

Executing Workflows

Sipke Schoorstra
Geek Culture
5 min readAug 3, 2021

--

In the previous part, we did the grunt work of setting up a basic ASP.NET Core web app project with UI, domain models and data access. We can now upload documents and store information in a database.

In this part, we will see how we can invoke workflows in response to uploaded files. In doing so, we can do all sorts of interesting things, such as sending the file for review as an email attachment, computing a hash, archive it, and whatever else floats your boat.

All of these things can be done without a workflows engine of course, but using a workflow engine offers a number of key advantages such as:

  • Coordinating automated steps with human interaction becomes as simple as clicking together a single workflow. The key advantage here being that the state of this long-running process is handled for you.
  • Adding, updating and removing processes can be done without needing to redeploy the application (if you wanted to — you can still make workflows part of your application as we will see).
  • The developer provides the building blocks (workflow steps, a.k.a. activities) enabling non-programmers to create and manage workflows.

Executing Workflows

Elsa provides various services to execute workflows, but at its core lies the IWorkflowRunner which takes a workflow blueprint and/or a workflow instance and the executes each activity one-by-one until either there are no more activities to be executed or until a blocking activity is encountered.

For our scenario, we want to execute workflows asynchronously in the background when a file is uploaded. To execute workflows in the background (using a queue worker for example), we use the IWorkflowDispatcher service.

This service will schedule a message on a queue (which is in-memory by default and uses Rebus), which is then picked up by a message consumer, which in turn uses the IWorkflowRunner service to actually execute the workflow.

To see how this works, let’s first create a simple do-nothing workflow and then invoke this workflow in response to a file upload.

Create HelloFile Workflow

With your application and Elsa Studio running, create a workflow named HelloFile and add a single Write Line activity with the following Liquid expression:

Hello File {{ Input }}! 

Input will reference the object that we will sent as an input when invoking the workflow. Publish your workflow and let’s take a look at invoking it next.

Run HelloFile Workflow

As you may remember, in the previous part we created a service called DocumentService that will publish the NewDocumentReceived event anytime its SaveDocumentAsync is invoked (which is done from the IndexModel class in the web project upon uploading a file).

Now we are about to see how convenient that setup is. To handle the event, create a new folder called Handlers in the DocumentManagement.Workflows project and add to it the following class:

The handler shown above does two things:

  1. Get a published workflow blueprint by name.
  2. Dispatch the workflow.

Before this handler is recognized, we need to register it with the service container. To do so, open the ServiceCollectionExtensions class in the same project and chain the following line in the AddWorkflowServices method:

services.AddNotificationHandlersFrom<StartHelloFileWorkflow>()

This will register any & all notification handlers found in the current assembly.

Test HelloFile Workflow

To make sure everything works, start your web app and upload a file. After a short while (anywhere between 0 and 15 seconds), you should see something like the following output appear in the console window:

Great! We now know how to run workflows whenever a document is uploaded.

Next, let’s see how we can select workflows for a given document type.

Workflows By Document Type

If we come up with a mechanism that allows us to associate a document type with a workflow, our application will allow users to add arbitrary document types and associated workflows to handle these types without us having to make any changes (unless of course the user wants to implement a workflow with new capabilities for which we don’t have an activity yet).

An easy way to associate workflows with document types is by tagging the workflow.

For example, we have a document type with ID "ChangeRequest". Let’s update or HelloFile workflow’s settings and enter this type ID in its Tag field (under the Advanced tab):

Next, let’s update the our event handler class as follows:

  • Rename to StartDocumentWorkflows.
  • Select ALL workflows where their tag matches the document type ID of the uploaded document.
  • Instead of sending the document ID as workflow input, let’s use the document ID as the correlation ID for the workflow. This will be useful when implementing long-running workflows as we will see in the next part of this series.

The final result should look like this:

Let’s also update the HelloFile workflow’s Write Line activity to print out the correlation ID instead of the workflow input (which we no longer send):

Hello File {{ CorrelationId }}!

When you now try uploading documents, notice that the HelloFile workflow will only be executed when you upload documents of type ChangeRequest.

Summary

In this part, we’ve seen how to execute workflows in the background in response to a domain event (NewDocumentReceived) and how to associate workflows with document types via the Tag attribute.

Next

In the next part, we will take a look at building out a few custom activities which we need to implement the workflows described in the introduction of this series.

--

--