MultiTenant.AspNetCore
0.1.3
dotnet add package MultiTenant.AspNetCore --version 0.1.3
NuGet\Install-Package MultiTenant.AspNetCore -Version 0.1.3
<PackageReference Include="MultiTenant.AspNetCore" Version="0.1.3" />
paket add MultiTenant.AspNetCore --version 0.1.3
#r "nuget: MultiTenant.AspNetCore, 0.1.3"
// Install MultiTenant.AspNetCore as a Cake Addin #addin nuget:?package=MultiTenant.AspNetCore&version=0.1.3 // Install MultiTenant.AspNetCore as a Cake Tool #tool nuget:?package=MultiTenant.AspNetCore&version=0.1.3
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.
Disable automatic tenant resolution middleware registration
By default the library will attempt to resolve the current tenant at the start of the middleware pipeline so that the tenant context is available as early as possible.
However, if your tenant resolution strategy cannot resolve a tenant identifier this early on you can disable this behvaior and manually specify when tenant resolution should be attempted using app.UseMultiTenancy<TenantInfo>()
First disable automatic registration
services.AddMultiTenancy<TestTenant>(o => { o.DisableAutomaticPipelineRegistration = true; })
The specify where the tenant resolution should be attempted
app.UseThis();
app.UseMultiTenancy<TestTenant>(); //Tenant resolution strategy used here
app.UseThat();
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 | Versions 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. |
-
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.3 | 981 | 5/4/2024 |
0.1.2 | 121 | 5/4/2024 |
0.1.1 | 123 | 4/18/2024 |
0.1.0-alpha | 115 | 4/17/2024 |
Add support for providing more control over the position in the ASP.NET Core middleware pipeline that
- the tenant is resolved using the configured resolution strategy
- the tenant specific services become available
**Full Changelog**: https://github.com/myquay/MultiTenant.AspNetCore/compare/0.1.2...0.1.3