MultiTenant.AspNetCore 0.1.1

dotnet add package MultiTenant.AspNetCore --version 0.1.1
NuGet\Install-Package MultiTenant.AspNetCore -Version 0.1.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MultiTenant.AspNetCore" Version="0.1.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add MultiTenant.AspNetCore --version 0.1.1
#r "nuget: MultiTenant.AspNetCore, 0.1.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install MultiTenant.AspNetCore as a Cake Addin
#addin nuget:?package=MultiTenant.AspNetCore&version=0.1.1

// Install MultiTenant.AspNetCore as a Cake Tool
#tool nuget:?package=MultiTenant.AspNetCore&version=0.1.1

Project Logo MultiTenant.AspNetCore

Multi-tenancy support for ASP.NET Core 8

About

A lightweight, easy to configure, open-source library which allows you to build multi-tenanted applications in ASP.NET Core 8.

It supports

  • Tenant resolution
  • Per-tenant service registration with the ASP.NET Dependency Injection
  • Per-tenant options registration
  • Per-tenant pipeline configuration

A deep-dive on the library internals is available here: https://michael-mckenna.com/multi-tenant-asp-dot-net-8-tenant-resolution/

Quickstart

The library is designed to follow common ASP.NET Core patterns for ease of configuration.

Installation

The library is distributed as a NuGet package: https://www.nuget.org/packages/MultiTenant.AspNetCore/ you can install it using your favourite package manager, or download the source and compile it locally.

Define how your application manages tenants

Multi-tenant requirements vary widely by use-case, this library provides the following extension points to cater for a wide range of usecases

  • What information is required in the tenant context - ITenantInfo
  • How tenants are identified (e.g. by domain) - ITenantResolutionStrategy
  • How tenant information is stored (e.g. in a database) - ITenantLookupService<>
Tenant data (ITenantInfo)

Implement the ITenantInfo interface to define your tenant specific data.

public class TenantInfo : ITenantInfo
{
  public required string Id { get; set; }
  public required string Name { get; set; }
  ... other properties ...
}
Tenant identification (ITenantResolutionStrategy)

Implement the ITenantResolutionStrategy to define how tenants are identified in your system, a common pattern is to give each tenant a different subdomain making the host a good candidate as an identifier. Here is an exmaple of how to implement a resolution strategy based on hostname.

public class HostResolutionStrategy(IHttpContextAccessor httpContextAccessor) : ITenantResolutionStrategy
{
    private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;

    public async Task<string> GetTenantIdentifierAsync()
    {
        if (_httpContextAccessor.HttpContext == null)
            throw new InvalidOperationException("HttpContext is not available");

        return await Task.FromResult(_httpContextAccessor.HttpContext.Request.Host.Host);
    }
}
Tenant lookup (ITenantLookupService<>)

Implement the ITenantLookupService<TenantInfo> interface to define how your application loads tenant configuration. This could be from memory, configuration, a database, or other durable data store depending on your requirements.

The lookup service accepts the identifier returned from your tenant resolution strategy to find the tenant.

 public class TenantLookupService() : ITenantLookupService<TenantInfo>
 {
     public Task<TenantInfo> GetTenantAsync(string identifier)
     {
         ... your implementation ...
     }
 }

Basic configuration

Configure your application to support multi-tenancy in the same place you register all your middleware and services.

//Add the library to your application and define the tenant data available in your tenant context 
builder.Services.AddMultiTenancy<TenantInfo>()
    //Specify your resolution strategy
    .WithResolutionStrategy<HostResolutionStrategy>()
    //Specify your tenant data provider 
    .WithTenantLookupService<TenantLookupService>();

You're done, whenever you want to access the current tenant just inject IMultiTenantContextAccessor<TenantInfo> using ASP.NET Core DI and you'll have access to the current tenant.

Advanced configuration

Per-tenant services & options

The library supports configuring services or options differently for different tenants, this allows you to do things such as register a database context with a seperate connection string etc.

    ///Add a service configured different per-tenant
    .WithTenantedServices((services, tenant) =>
    {
       if (tenant != null)
           services.AddSingleton<SomeService>(options =>
           {
               options.SomeSetting = tenant.SomeTenantSpecificSetting;
           });
    })
    ///Register different options per-tenant (e.g. different localisations)
    .WithTenantedConfigure<RequestLocalizationOptions>((options, tenant) =>
    {
        var supportedCultures = tenant?.CultureOptions ?? ["en-NZ"];
        options.SetDefaultCulture(supportedCultures[0])
            .AddSupportedCultures(supportedCultures)
            .AddSupportedUICultures(supportedCultures);
    
    });
Per-tenant pipeline

The library also supports modifying the middleware pipeline based on tenant. This allows different tenants to load completely different middleware and can be especially useful if a middleware captures its configuration on startup.

For example the localisation middleware caches its configuration on start-up so by the time a request comes in we cannot alter it for that tenant's configuration. By having a tenant specific pipeline the configuration options captured by the middleware on start-up is now tenant specific.

In the example below, the application will now respect the localisation of each tenant even though the middleware does not allow configuration to change after startup.

app.UseMultiTenantPipeline<TenantOptions>((tenant, app) =>
{
    app.UseRequestLocalization();
});

This makes our library compatible with a wide range of existing middleware without the need of additional work-arounds.

Roadmap

  • Tenant resolution
  • Per-tenant services (Dependency injection)
  • Per-tenant options
  • Per-tenant pipeline
  • Per-tenant data-isolation with EF Core
  • Per-tenant authentication

Contributing

Contributions are very welcome!

Ways to contribute

  • Fix an existing issue and submit a pull request
  • Review open pull requests
  • Report a new issue
  • Make a suggestion/ contribute to a discussion

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.1 78 4/18/2024
0.1.0-alpha 87 4/17/2024

Initial release of [MultiTenant.AspNetCore](https://github.com/myquay/MultiTenant.AspNetCore)