Databases Supported by Duende Storage
Duende Storage supports SQL Server, PostgreSQL, Oracle and SQLite. This page compares them, explains how provider registration and schema deployment work the same way across all of them, and links to each provider’s page for connection strings, custom schemas and provider-specific notes.
Choose a Database
Section titled “Choose a Database”| Database | Package | Registration method | Connection object you register | Schema option | CLI --provider value | Intended use |
|---|---|---|---|---|---|---|
| SQL Server | Duende.Storage.MsSql | AddMsSql | CreateSqlConnection factory | SchemaName | mssql | Production |
| PostgreSQL | Duende.Storage.PostgreSql | AddPostgreSql | NpgsqlDataSource | SchemaName | postgresql | Production |
| Oracle | Duende.Storage.Oracle | AddOracle | CreateOracleConnection factory | SchemaName | Not supported yet (run migrations without the CLI) | Production |
| SQLite | Duende.Storage.Sqlite | AddSqlite | A connection string | None | sqlite | Automated tests and local development only |
Duende Storage doesn’t recommend any particular database. Choose the database that fits your team’s operations and hosting environment. SQL Server, PostgreSQL and Oracle are all production-ready; SQLite is well-suited for automated tests and local development only.
How To Register A Duende Storage Provider
Section titled “How To Register A Duende Storage Provider”Every provider follows the same registration shape:
AddStorage(...)selects exactly one database provider for a storage instance. Calling it with a second provider for the same instance throws anInvalidOperationException.- For SQL Server, PostgreSQL and Oracle, your application owns the connection factory or data source: a
CreateSqlConnectionorCreateOracleConnectiondelegate, or anNpgsqlDataSource. You control credentials, token providers and pooling settings on it. Duende Storage never disposes an object you registered this way. - The default-instance overloads (for example
AddMsSql(Action<MsSqlStorageEngineOptions>)) resolve that object unkeyed from dependency injection, and only work for the default storage instance. Named instances must use the resolver overload (for exampleAddMsSql(Func<IServiceProvider, CreateSqlConnection> resolver, ...)), which resolves the object however you configure it, including from a keyed registration. See Multiple Storage Instances for how to register and migrate more than one storage instance.
Create and Upgrade the Schema
Section titled “Create and Upgrade the Schema”Duende Storage separates two kinds of schema change:
- Document-level changes, such as adding a custom property to a client or resource, need no migration. The data extension schemas validate documents, not database tables.
- The fixed set of storage tables (entities, indexes, the outbox, and the schema version itself) is created and upgraded by Duende Storage’s own migrations, through the APIs below.
IStorageInstanceSchema gives you those migration operations for a storage instance:
-
MigrateAsync()creates the schema if it doesn’t exist, upgrades it if it’s behind, or does nothing if it’s already current. It then callsVerifySchemaAsyncand throws anInvalidOperationExceptionif verification finds errors. -
CheckVersionAsync()returns aCheckSchemaVersionResultwithCurrentVersion,RequiredVersionandIsCompatible, so you can fail fast beforeMigrateAsyncinstead of discovering a mismatch at request time:Program.cs using Duende.Storage.Schema;var schema = app.Services.GetRequiredService<IStorageInstanceSchema>();var versionCheck = await schema.CheckVersionAsync(CancellationToken.None);if (!versionCheck.IsCompatible){throw new InvalidOperationException($"Database schema version {versionCheck.CurrentVersion} does not match the " +$"required version {versionCheck.RequiredVersion}.");}IsCompatibleis false whenCurrentVersionandRequiredVersiondiffer. An unreachable database throws an exception rather than returning a result withIsCompatibleset to false:CurrentVersionof0means the database has never been migrated: run the migration.CurrentVersionless thanRequiredVersionmeans the database is behind: run the migration.CurrentVersiongreater thanRequiredVersionmeans a newer version of Duende Storage already migrated the database. Upgrade the application to a version that expects the current schema instead of trying to downgrade the schema.
-
VerifySchemaAsync()checks that every table, index and other object the current schema version expects is present, without changing anything. -
BuildMigrationScript(DatabaseSchemaVersion fromVersion)returns the SQL for the given starting version without opening a database connection. PassDatabaseSchemaVersion.Zerofor the full script from an empty database.
app.Services.GetRequiredService<IStorageInstanceSchema>() resolves the default storage instance only. For a named
instance, use IStorageInstanceSchemaFactory.GetStorageInstanceSchema(storageInstanceId, cancellationToken) to resolve its schema,
as shown in Multiple Storage Instances.
If a Migration Fails
Section titled “If a Migration Fails”On SQL Server, PostgreSQL and SQLite, each version step runs inside a transaction, so a failed step rolls back and running the migration again resumes from the last completed version. On Oracle, DDL commits automatically, so a failed step can leave some objects already created; the migration scripts skip objects that already exist, so running the migration again is safe there too. After migrating, Duende Storage verifies the schema and throws if verification finds a mismatch.
Keep Connection Strings Secret
Section titled “Keep Connection Strings Secret”Don’t commit connection strings or store them in plain configuration files. Options that keep secrets out of source control and shell history include:
- Environment variables, for example
ConnectionStrings__IdentityServer, whichIConfigurationbinds automatically. - Azure Key Vault or another vault-backed configuration provider.
dotnet user-secretsfor local development.- A passwordless or managed-identity connection, where the driver supports one, so no password needs to be stored at all.
For the Duende CLI, prefer the DUENDE_STORAGE_CONNECTION_STRING environment variable over --connection-string, so
the connection string doesn’t appear in shell history or process listings.
Connection Pooling
Section titled “Connection Pooling”The SQL Server, PostgreSQL and Oracle drivers all pool connections by default and expose pool-sizing keywords on the
connection string or connection object you register: Min Pool Size/Max Pool Size for SQL Server, Minimum Pool Size/Maximum Pool Size for PostgreSQL, and Min Pool Size/Max Pool Size for Oracle. Tune these on the connection
string; see each provider’s page for the keywords it supports. SQLite is for automated tests and local development
only, so connection pooling isn’t a concern there.