Dapper Setup
Configure Elsa Workflows 3.8.1 with the Dapper persistence provider, including SQL dialect selection and FluentMigrator schema setup.
Elsa's Dapper extension supplies relational persistence implementations for workflow management, workflow runtime, and (optionally) Elsa Identity. It uses a connection provider to create database connections and select the SQL dialect used by the stores.
This page targets the release/3.8.1 source. The extension contains built-in connection providers for SQLite, SQL Server, and PostgreSQL. The extension does not contain a MySQL connection provider; choosing MySQL in an unrelated Elsa EF Core example does not configure Dapper.
Choose Dapper when
Dapper is a good fit when your team wants Elsa's store contracts backed by direct SQL and already operates a supported relational database. Choose EF Core when you want Elsa's EF migrations and provider-specific EF tooling. Choose MongoDB when a document database is a better fit for your deployment.
Dapper is not a second workflow model: it replaces the persistence implementations behind Elsa's existing management and runtime contracts. The 3.8.1 extension wires these stores:
Management
Workflow definitions and workflow instances
Runtime
Triggers, bookmarks, bookmark queue items, workflow execution logs, activity execution records, and key/value records
Identity (optional)
Users, applications, and roles
Install the packages
Add the Dapper extension to the server project:
dotnet add package Elsa.Persistence.Dapper --version 3.8.1The extension references the separate Elsa.Persistence.Dapper.Migrations assembly. Add that package explicitly when your host refers to the migration types, as the examples below do:
dotnet add package Elsa.Persistence.Dapper.Migrations --version 3.8.1The extension's built-in connection providers use these ADO.NET drivers:
SQLite
SqliteDbConnectionProvider
Microsoft.Data.Sqlite
SQL Server
SqlServerDbConnectionProvider
Microsoft.Data.SqlClient
PostgreSQL
PostgreSqlDbConnectionProvider
Npgsql
For automatic SQLite or SQL Server migrations, also reference the matching FluentMigrator runner package in the host:
Use only the runner package for the database you select. The Dapper extension source and the 3.8.1 workbench explicitly configure AddSQLite() or AddSqlServer(); PostgreSQL has a Dapper connection provider, but the workbench does not ship a PostgreSQL FluentMigrator runner configuration.
Configure a durable SQL Server store
The following is the registration shape used by the 3.8.1 workbench. Keep the connection string in configuration or a secret store; do not commit a real password.
UseIdentity(identity => identity.UseDapper()) is needed only when the Elsa Identity stores should use Dapper. Omit it when the host uses another identity store or an external identity system.
The management and runtime calls are the important workflow registrations. They select Dapper implementations for the existing Elsa store contracts; they do not configure activity-specific data stores.
SQLite variant
Use a file-backed SQLite connection for persistence that must survive process restarts:
The feature's default provider is an in-memory SQLite provider with the connection string Data Source=:memory:;Cache=Shared. Always set an explicit provider for a deployed application. The in-memory default is useful for short-lived tests, not durable workflow execution.
PostgreSQL and custom providers
PostgreSQL data access is supported through PostgreSqlDbConnectionProvider:
The provider supplies both an NpgsqlConnection and the matching PostgreSqlDialect. If you use a database that is not represented by a built-in provider, implement IDbConnectionProvider and return an ISqlDialect whose SQL is valid for that database. Register that provider via dapper.DbConnectionProvider.
Do not assume that a connection provider alone makes the bundled migrations portable. Automatic migrations require a FluentMigrator runner provider and the migration SQL must be verified against the target database.
Connection lifetime and transactions
Each built-in provider creates a new ADO.NET connection when an Elsa Dapper store needs one, and the store disposes it after the operation. Configure the provider with the connection string appropriate for your driver; do not keep a single open connection in application-wide state.
The Elsa Dapper store methods do not expose a shared transaction parameter. Do not assume that several calls across different Elsa stores are one atomic transaction. If an application needs a cross-store transaction, design and test that integration explicitly against the selected database provider.
Migrations and schema ownership
Calling UseMigrations() adds FluentMigrator and registers RunMigrationsStartupTask. The task calls MigrateUp() when the host starts. The configured runner must therefore include all of the following:
A database-specific runner such as
AddSQLite()orAddSqlServer().The connection string from the registered
IDbConnectionProvider.The assembly containing
Elsa.Persistence.Dapper.Migrations.
The bundled migration assembly groups versions by module prefix:
Management
1000
Initial through V3_4
Runtime
2000
Initial through V3_7
Identity
3000
Initial through V3_3
Migration versions are not the Elsa package version. For example, Management Initial is migration 10001, and Runtime V3_7 is migration 20007.
Choose one schema owner for each environment:
Let the controlled application startup run the bundled migrations, or
apply reviewed migrations from the same migration assembly before starting the application.
Do not copy the old 3.7.0 snippets from this repository or mix Dapper migrations with EF Core migrations. If you manage PostgreSQL or another custom database externally, verify the generated SQL, indexes, data types, and migration history against your exact database engine.
Interrupted-workflow recovery
The 3.8.1 Dapper workflow-instance store supports the conditional TryMarkInterruptedAsync update used by force-drain recovery. Apply the workflow-instance schema before enabling recovery, and keep workflow instances, bookmarks, and execution logs in durable storage when they must survive a process restart. See Persistence provider recovery support for the deployment checklist.
SQL dialect behavior
Every IDbConnectionProvider exposes a matching ISqlDialect. The stores use that dialect for filtering, ordering, pagination, inserts, updates, and upserts. The built-in differences include:
SQL Server uses
OFFSET ... ROWSandFETCH NEXT ... ROWS ONLY.SQLite uses
LIMIT ... OFFSET ...andINSERT OR REPLACE.PostgreSQL uses
ON CONFLICT (...) DO UPDATEfor upserts.
This is why changing only the connection string is insufficient when adding a custom database. The provider and dialect must agree, and the migration runner must target the same engine.
Operational checklist
Use a durable database and an explicit
DbConnectionProvideroutside tests.Keep the Elsa Dapper package, migration assembly, and runner package on compatible versions.
Give the startup identity permission to apply migrations, or run migrations in a separate deployment step with an appropriately privileged identity.
Coordinate migration startup when multiple application instances can start simultaneously; the source registers a startup task but does not provide a distributed migration lock.
Test workflow resume, bookmarks, triggers, logs, and tenant-scoped data after changing providers or dialects.
Parameterize application SQL. Dapper's own guidance recommends passing values as parameters rather than concatenating them into SQL; Elsa's internal Dapper query builder follows the same pattern.
Elsa Studio boundary
Dapper configuration is server-side. The 3.8.1 Studio source has no Dapper-specific persistence implementation or provider-selection screen. Once the server's management and runtime APIs use Dapper, Studio consumes those existing APIs; migrations, database credentials, dialect selection, and schema ownership remain responsibilities of the server host.
Release source references
Last updated