AI Workflows in .NET with Elsa 3 (Step-by-Step Implementation)
Elsa is an open-source workflows library that can be used in any kind of .NET Core application. Using such a workflow library can be useful to implement business rules visually or programmatically...
In this article, we'll explore how to integrate Elsa 3, the powerful workflow engine in a .NET application. This is a continuation of my previous post where I covered Elsa 2.x integration a long time ago.
Here, we'll dive into the latest version and create an AI-powered workflow example.
Prefer watching instead? Check out the video below 👇
Scenario
In this tutorial, we'll build an AI-powered question-answering system using Elsa workflows. Our workflow will accept questions via HTTP endpoints and use Ollama's LLM capabilities to generate responses. This demonstrates how to combine ABP's enterprise features with Elsa's workflow management and AI integration.
Let's get started!
Step 1: Creating the ABP Application
First, let's create a new ABP application with MVC and PostgreSQL:
abp new AbpWorkflowDemo -t app -u mvc -d ef -dbms PostgreSQL
Or alternatively, you can use the ABP Studio UI to create the project: https://abp.io/docs/latest/get-started/layered-web-application?UI=MVC&DB=EF&Tiered=No
Step 2: Adding Elsa 3 Dependencies
Add the following NuGet packages to your AbpWorkflowDemo.Web
project:
dotnet add package Elsa
dotnet add package Elsa.EntityFrameworkCore.PostgreSql
dotnet add package Elsa.Workflows.Api
These packages provide:
Core Elsa functionality
PostgreSQL persistence
API endpoints for workflow management
Step 3: Configuring Elsa in ABP
Let's configure Elsa in the AbpWorkflowDemoWebModule.cs
file. Here's the configuration with detailed explanations:
//... | |
public class AbpWorkflowDemoWebModule : AbpModule | |
{ | |
//... | |
public override void ConfigureServices(ServiceConfigurationContext context) | |
{ | |
context.Services.AddElsa(elsa => | |
{ | |
// Configure Management Layer to use EF Core. | |
elsa.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef => ef.UsePostgreSql(configuration.GetConnectionString("Default")!))); | |
// Configure Runtime Layer to use EF Core. | |
elsa.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore(ef => ef.UsePostgreSql(configuration.GetConnectionString("Default")!))); | |
// Register custom activities from the application | |
elsa.AddActivitiesFrom<AbpWorkflowDemoWebModule>(); | |
// Register custom workflows from the application | |
elsa.AddWorkflowsFrom<AbpWorkflowDemoWebModule>(); | |
// Expose Elsa API endpoints. | |
elsa.UseWorkflowsApi(); | |
// Enable HTTP activities. | |
elsa.UseHttp(); | |
}); | |
} | |
//... | |
} |
This configuration:
Sets up PostgreSQL as the database for workflow persistence
Enables workflow management and runtime features
Registers custom activities and workflows
Exposes API endpoints for workflow management
Enables HTTP activities for web-based workflows
Add the following middleware to the OnApplicationInitialization
method:
app.UseRouting(); | |
app.UseWorkflowsApi(); // Use Elsa API endpoints. | |
app.UseWorkflows(); // Use Elsa middleware to handle HTTP requests mapped to HTTP Endpoint activities. | |
app.UseAbpSecurityHeaders(); |
After configuring Elsa with PostgreSQL, the Elsa workflow library automatically creates several tables to manage workflows, activities, and their execution states. Let's run our application to see these tables in action:
dotnet run --project AbpWorkflowDemo.Web
Once the application is running, you can check your PostgreSQL database to see the following tables:
These tables are essential for Elsa's operation:
WorkflowDefinitions
: Stores workflow definitionsWorkflowInstances
: Tracks running workflow instancesWorkflowExecutionLogRecords
: Maintains execution historyBookmarks
: Manages workflow bookmarks for resuming workflowsTriggers
: Stores workflow triggers...
These tables will be populated as we create and run workflows in the next sections. The database structure ensures that our workflows are persistent and can be resumed even after application restarts.
Step 4: Creating an AI-Powered Workflow
Let's create an interesting example that combines Elsa Workflows with AI capabilities. We'll use Ollama with the Llama 3.1 model to create a question-answering workflow.
First, install the Ollama package (don't forget to use the prerelease version (--prerelease
)):
dotnet add package Microsoft.Extensions.AI.Ollama --prerelease
Start the Ollama server:
ollama run llama3.1
If you haven’t used Ollama before, you should first download it to your PC (https://ollama.com/download). After setting Ollama up, then you can pull the model with the command: `ollama pull llama3.1`.
Also, you can check my video to set-up Ollama and using it in a .NET application:From Text to Summary: LLaMA 3.1 + .NET in Action!
·In this video, we build a complete Blazor WebAssembly app that connects to a local LLaMA 3.1 model via Microsoft.Extensions.AI.Ollama to summarize any given text — right in your browser! No cloud dependencies, no third-party APIs — just pure .NET + local AI magic. 🧠✨
Now, let's create our AI-powered workflow. Create a new file AIQuestionAnsweringWorkflow.cs
in the Workflows folder:
using System; | |
using System.Collections.Generic; | |
using System.Net; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Elsa.Http; | |
using Elsa.Workflows; | |
using Elsa.Workflows.Activities; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.Extensions.AI; | |
namespace AbpWorkflowDemo.Web.Activities | |
{ | |
public class AIQuestionAnsweringWorkflow : WorkflowBase | |
{ | |
protected override void Build(IWorkflowBuilder builder) | |
{ | |
var routeDataVariable = builder.WithVariable<IDictionary<string, object>>(); | |
var questionVariable = builder.WithVariable<string>(); | |
var answerVariable = builder.WithVariable<string>(); | |
builder.Root = new Sequence | |
{ | |
Activities = | |
{ | |
// Define the HTTP endpoint / we will process this URL: /ask-question/<some-question> | |
new HttpEndpoint | |
{ | |
Path = new("ask-question/{question}"), | |
SupportedMethods = new(new[] { HttpMethods.Get }), | |
CanStartWorkflow = true, | |
RouteData = new(routeDataVariable) | |
}, | |
// Set the question variable to the question from the URL | |
new SetVariable | |
{ | |
Variable = questionVariable, | |
Value = new (context => | |
{ | |
var routeData = routeDataVariable.Get(context); | |
return routeData["question"]!.ToString(); | |
}) | |
}, | |
// Send the text to the LLM and get response and return as a HTTP response | |
new WriteHttpResponse | |
{ | |
StatusCode = new(HttpStatusCode.OK), | |
Content = new (async context => | |
{ | |
var client = new OllamaChatClient( | |
new Uri("http://localhost:11434"), | |
modelId: "llama3.1" | |
); | |
var question = questionVariable.Get(context); | |
var answer = await client.GetResponseAsync($"Answer the following question: {question}"); | |
return answer.Text; | |
}) | |
} | |
} | |
}; | |
} | |
} | |
} |
Let's break down this workflow:
Variables Setup:
routeDataVariable
: Stores the route parametersquestionVariable
: Holds the question from the URLanswerVariable
: Stores the AI's response
Workflow Activities:
HttpEndpoint
: Creates an HTTP endpoint that accepts GET requestsSetVariable
: Extracts the question from the URL parametersWriteHttpResponse
: Sends the question to Ollama and returns the response
When we run our application, Elsa will automatically discover and register this workflow. As we make HTTP requests to our endpoint, Elsa will create workflow instances and store their execution state in the database tables we saw earlier. This includes tracking the workflow's progress, storing variables, and maintaining execution history.
Let's run the application and verify our setup:
dotnet run --project AbpWorkflowDemo.Web
Once the application is running, you can check your PostgreSQL database to see the workflow-related tables being populated:
Testing the Workflow
You can test the workflow by sending a GET request to:
http://localhost:<port>/workflows/ask-question/what+is+the+capital+of+france
Here is the video of the workflow in action:
The workflow:
Receives the question
Processes it through the Llama 3.1 model
Returns the AI-generated response
Alternative: Programmatic Workflow Execution
You can also trigger the workflow programmatically using the IWorkflowRunner
service:
private readonly IWorkflowRunner _workflowRunner;
var workflow = await _workflowRunner.RunAsync<AIQuestionAnsweringWorkflow>();
Using Elsa Studio
For a visual workflow design experience, you can use Elsa Studio. It's a Blazor-based UI that connects to your Elsa server.
To run Elsa Studio using Docker:
docker pull elsaworkflows/elsa-studio-v3-4-0-preview:latest
docker run -t -i -e ASPNETCORE_ENVIRONMENT='Development' -e HTTP_PORTS=8080 -e ELSASERVER__URL=http://localhost:13000/elsa/api -p 14000:8080 elsaworkflows/elsa-studio-v3:latest
This will run Elsa Studio on port 14000. For more details on setting up Elsa Studio locally, refer to the official documentation.
Stay tuned for an upcoming post where I'll provide a detailed guide on running Elsa Studio locally and designing workflows through its intuitive UI. Let me know in the comments if you'd like to see specific aspects covered!
Conclusion
In this article, we've explored how to integrate Elsa 3 with the ABP Framework and created an AI-powered workflow example. The combination of Elsa's powerful workflow capabilities with ABP's robust framework provides a solid foundation for building complex business processes. The AI integration example demonstrates how easily you can extend workflows with modern AI capabilities.
Stay tuned for our next article where we'll dive deeper into Elsa Studio and explore more advanced workflow scenarios!
Great and comprehensive guide!
Thank you for your valuable contribution to the .NET ecosystem as a community member. 🙌