Workflow tools for .Net applications

Tohid haghighi
6 min readJan 17, 2024

A workflow is a long-running process that is composed of a series of interconnected activities. Workflows allow developers to define and execute complex processes that may span multiple services or even systems. Workflows are executed by the Temporal workflow engine.

2 of most famous workflow tools are Elsa and Temporal.

Temporal very famous than Elsa but Elsa very easy to use against of Temporal.

How do I create an Elsa workflow?

We can create ELSA Workflows using C# and JSON. It also has a Workflow Designer in the form of an HTML Web Component. ELSA has reusable components using which we can manage workflows and activities. We can also run ELSA Workflows within our application using simple-to-use APIs.

Elsa workflow

In this quickstart, we will take a look at a minimum ASP.NET Core application that executes a workflow. The workflow will listen for an incoming HTTP request and write back a simple response.

We will:

  • Create an ASP.NET Core application.
  • Programmatically define a workflow definition using Elsa’s Workflow Builder API that executes when an HTTP request comes in at a specified URL.

The Project

Create a new, empty ASP.NET Core project called ElsaQuickstarts.WebApp.HelloWorld:

dotnet new web -n "ElsaQuickstarts.WebApp.HelloWorld"

CD into the created project folder:

cd ElsaQuickstarts.WebApp.HelloWorld

Add the following packages:

dotnet add package Elsa
dotnet add package Elsa.Activities.Http
dotnet add package Elsa.Designer.Components.Web

Using Elsa workflow as Midddleware

using Elsa;
using Elsa.Persistence.EntityFramework.Core.Extensions;
using Elsa.Persistence.EntityFramework.SqlServer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
// Add Elsa services
services.AddElsa(elsa => elsa
.UseEntityFrameworkPersistence(ef => ef.UseSqlServer(Configuration.GetConnectionString("ElsaContext")))
.AddConsoleActivities()
.AddHttpActivities())
.AddWorkflow<OrderProcessingWorkflow>()); // Register your custom workflow

// Add the Elsa Dashboard services
services.AddElsaDashboard();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// other configurations...

// Enable middleware to serve Elsa Dashboard
app.UseStaticFiles(); // For the dashboard's static files
app.UseRouting();

app.UseEndpoints(endpoints =>
{
// other endpoint configurations...

endpoints.MapControllers();

// Map Elsa Dashboard
endpoints.MapElsaDashboard();
});
}
}

First of all you must draw your code flow in the charts and implement it with elsa. for example my code must have a flow with 3 steps you can implement it with below code.

public class PaymentWorkflow : IWorkflow
{
public void Build(IWorkflowBuilder builder)
{
builder
.StartWith<ValidateAccountNumberActivity>()
.Then<GetUserInfoActivity>()
.Then<CheckDoNotPayBeforeActivity>()
.Then<PaymentActivity>()
.Then<SendSmsActivity>();
}
}

public class ValidateAccountNumberActivity: Activity
{
// Implementation of check account exist
}

public class GetUserInfoActivity: Activity
{
// Implementation of Get userinfo for send to bank
}

public class CheckDoNotPayBeforeActivity: Activity
{
// Implementation of check do not pay before
}

public class PaymentActivity: Activity
{
// Implementation of payment
}

public class SendSmsActivity: Activity
{
// Implementation of sending payment sms
}

Workflow Concepts

In order to work effectively with Elsa, it’s important to understand its terminology. Below is a list of words that represent important concepts used in Elsa.

Workflow

A workflow consists of a series of steps called activities that are connected to one another. A workflow maintains all sorts of information, such as the following:

  • Which activity is currently executing.
  • What variables are set.
  • What activities are blocking further execution.

Once an activity is done executing, the workflow checks its outcome and if there’s another activity connected to it. If so, that activity is scheduled for execution.

This goes on until there are either no more activities to execute, or an activity is encountered that instructs the workflow runner to suspend the workflow.

Activity

An activity is an atomic building block that represents a single executable step on the workflow. At a bare minimum, an activity implements the OnExecute method, which contains the code to execute.

Starting Activity

An activity which is the starting point of the workflow and does not have any inbound connections. They are the entry points to the workflow.

Blocking Activity

When an activity executes, it returns an activity execution result, which is somewhat analogous to an MVC/API ActionResult. There are various possible results that can be returned, but the most commonly used ones are Done, Outcomes and Suspend.

When Suspend is returned (as is typically the case with blocking activities), the workflow will enter the Suspended state and the activity will be registered as a blocking activity.

Suspended Workflow

Suspended workflows are blocked by one or more blocking activities. The only way to resume such a workflow is to trigger it with the name of one of the blocking activities.

Connection

A connection represents a connection between two activities. This is how the workflow runner knows what activities to execute next. A connection between two activities holds 3 pieces of information:

  • The source activity ID.
  • The source outcome name (e.g. Done).
  • The target activity ID.

For each possible outcome of a given activity, a connection can be established from that outcome to another activity.

For example, let’s say we have a workflow with three activities called Activity A, Activity B and Activity C. Activity A has 2 outcomes called Done and Failed, and we wish to connect the Done outcome to Activity B and Failed to Activity C.

This means we need the following two connections:

Connection 1

  • Source: Activity A
  • Outcome: Done
  • Destination: Activity B

Connection 2

  • Source: Activity A
  • Outcome: Failed
  • Destination: Activity C

Visually, this would look like this:

Elsa workflow

Long Running Workflows

A long-running workflow is a workflow that doesn’t run from start to end in one go. Instead, it might have one or more blocking activities that will instruct the workflow engine to suspend the workflow until it receives the appropriate stimulus to resume execution.

Short Running Workflows

A short-running workflow is a workflow that, in contrast to long-running workflows, does run from start to end in one go.

Burst of Execution

A burst of execution refers to the execution of a sequence of activities one after another until either one of the following occurs:

  • No more activities were scheduled (the end of the workflow was reached), or
  • A blocking activity was encountered.

Introducing Temporal .NET — Deterministic Workflow Authoring in .NET

A Temporal workflow is code that is executed in a durable, reliable, and scalable way. Today Temporal allows you to write workflows in Go, Java, Python, TypeScript, and more. You can now add .NET to that list with the release of the .NET SDK. While this post will focus on C#, any .NET language will work.

Different language runtimes have different trade-offs for writing workflows. Go is very fast and resource efficient due to runtime-supported coroutines, but that comes at the expense of type safety (even generics as implemented in Go are limited for this use). Java is also very fast and type safe, but a bit less resource efficient due to the lack of runtime-supported coroutines (but virtual threads are coming). It might sound weird to say, but our dynamic languages of JS/TypeScript and Python are probably the most type-safe SDKs when used properly; however, as can be expected, they are not the most resource efficient. .NET provides the best of all worlds: high performance like Go/Java, good resource utilization like Go, and high quality type-safe APIs.

Webinar recording — Introducing Temporal .NET and how it was built.

This post will give a high-level overview of the .NET SDK and some interesting challenges encountered during its development. To get more info about the SDK, see:

Contents:

NOTE: A previous version of this post used a “Ref” pattern to invoke workflows, activities, signals, and queries. This has been updated to use the latest lambda expressions supported in current .NET SDK versions.

Temporal Dotnet

--

--

Tohid haghighi

Full-Stack Developer | C# | .NET Core | Vuejs | TDD | Javascript