Skip to content

The search box in the website knows all the secrets—try it!

For any queries, join our Discord Channel to reach us faster.

JasperFx Logo

JasperFx provides formal support for Wolverine and other JasperFx libraries. Please check our Support Plans for more details.

Multi-Tenancy with EF Core 4.0

Wolverine has first class support for using a single EF Core DbContext type that potentially uses different databases for different clients within your system, and this includes every single bit of EF Core capabilities with Wolverine:

  • Wolverine will manage a separate transactional inbox & outbox for each tenant database and any main database
  • The transactional middleware is multi-tenant aware for EF Core
  • Wolverine's Tenant id detection for HTTP is supported by the EF Core integration
  • The storage actions and [Entity] attribute support for EF Core will respect the multi-tenancy

Alright, let's get into a first concrete sample. In this simplest usage, I'm assuming that there are only three separate tenant databases, and each database will only hold data for a single tenant.

To use EF Core with multi-tenanted PostgreSQL storage, we can use this:

cs
var builder = Host.CreateApplicationBuilder();

var configuration = builder.Configuration;

builder.UseWolverine(opts =>
{
    // First, you do have to have a "main" PostgreSQL database for messaging persistence
    // that will store information about running nodes, agents, and non-tenanted operations
    opts.PersistMessagesWithPostgresql(configuration.GetConnectionString("main"))

        // Add known tenants at bootstrapping time
        .RegisterStaticTenants(tenants =>
        {
            // Add connection strings for the expected tenant ids
            tenants.Register("tenant1", configuration.GetConnectionString("tenant1"));
            tenants.Register("tenant2", configuration.GetConnectionString("tenant2"));
            tenants.Register("tenant3", configuration.GetConnectionString("tenant3"));
        });
    
    opts.Services.AddDbContextWithWolverineManagedMultiTenancy<ItemsDbContext>((builder, connectionString, _) =>
    {
        builder.UseNpgsql(connectionString.Value, b => b.MigrationsAssembly("MultiTenantedEfCoreWithPostgreSQL"));
    }, AutoCreate.CreateOrUpdate);
});

snippet source | anchor

And instead with multi-tenanted SQL Server storage:

cs
var builder = Host.CreateApplicationBuilder();

var configuration = builder.Configuration;

builder.UseWolverine(opts =>
{
    // First, you do have to have a "main" PostgreSQL database for messaging persistence
    // that will store information about running nodes, agents, and non-tenanted operations
    opts.PersistMessagesWithSqlServer(configuration.GetConnectionString("main"))

        // Add known tenants at bootstrapping time
        .RegisterStaticTenants(tenants =>
        {
            // Add connection strings for the expected tenant ids
            tenants.Register("tenant1", configuration.GetConnectionString("tenant1"));
            tenants.Register("tenant2", configuration.GetConnectionString("tenant2"));
            tenants.Register("tenant3", configuration.GetConnectionString("tenant3"));
        });
    
    // Just to show that you *can* use more than one DbContext
    opts.Services.AddDbContextWithWolverineManagedMultiTenancy<ItemsDbContext>((builder, connectionString, _) =>
    {
        // You might have to set the migration assembly
        builder.UseSqlServer(connectionString.Value, b => b.MigrationsAssembly("MultiTenantedEfCoreWithSqlServer"));
    }, AutoCreate.CreateOrUpdate);

    opts.Services.AddDbContextWithWolverineManagedMultiTenancy<OrdersDbContext>((builder, connectionString, _) =>
    {
        builder.UseSqlServer(connectionString.Value, b => b.MigrationsAssembly("MultiTenantedEfCoreWithSqlServer"));
    }, AutoCreate.CreateOrUpdate);
});

snippet source | anchor

Note in both samples how I'm registering the DbContext types. There's a fluent interface first to register the multi-tenanted database storage, then a call to register a DbContext with multi-tenancy. You'll have to supply Wolverine with a lambda to configure the DbContextOptionsBuilder for the individual DbContext object. At runtime, Wolverine will be passing in the right connection string for the active tenant id. There is also other overloads to configure based on a DbDataSource if using PostgreSQL or to also take in a TenantId value type that will give you the active tenant id if you need to use that for setting EF Core query filters like this example from the Microsoft documentation.

Combine with Marten

It's perfectly possible to use Marten and its multi-tenancy support for targeting a separate database with EF Core using the same databases. Maybe you're using Marten for event sourcing, then using EF Core for flat table projections. Regardless, you simply allow Marten to manage the multi-tenancy and the relationship between tenant ids and the various databases, and the Wolverine EF Core integration can more or less ride on Marten's coat tails:

cs
opts.Services.AddMarten(m =>
{
    m.MultiTenantedDatabases(x =>
    {
        x.AddSingleTenantDatabase(tenant1ConnectionString, "red");
        x.AddSingleTenantDatabase(tenant2ConnectionString, "blue");
        x.AddSingleTenantDatabase(tenant3ConnectionString, "green");
    });
}).IntegrateWithWolverine(x =>
{
    x.MasterDatabaseConnectionString = Servers.PostgresConnectionString;
});

opts.Services.AddDbContextWithWolverineManagedMultiTenancyByDbDataSource<ItemsDbContext>((builder, dataSource, _) =>
{
    builder.UseNpgsql(dataSource, b => b.MigrationsAssembly("MultiTenantedEfCoreWithPostgreSQL"));
}, AutoCreate.CreateOrUpdate);

snippet source | anchor

Released under the MIT License.