Finbuckle.MultiTenant Docs
A multitenant strategy is responsible for defining how the tenant is determined. It ultimately produces an identifier
string which is used to create a TenantInfo
object with information from the MultiTenant store.
Finbuckle.MultiTenant supports several "out-of-the-box" strategies for resolving the tenant. Custom strategies can be
created by implementing IMultiTenantStrategy
or using DelegateStrategy
.
The Strategy
property on the StrategyInfo
member of MultiTenantContext
instance returned
by HttpContext.GetMultiTenantContext()
returns the actual strategy used to resolve the tenant information for the
current context.
All multitenant strategies derive from IMultiTenantStrategy
and must implement the GetIdentifierAsync
method.
If an identifier can't be determined, GetIdentifierAsync
should return null which will ultimately result in a
null TenantInfo
.
Configure a custom implementation of IMultiTenantStrategy
by calling WithStrategy<TStrategy>
after AddMultiTenant<T>
in the ConfigureServices
method of the Startup
class. There are several available
overrides for configuring the strategy. The first override uses dependency injection along with any passed parameters to
construct the implementation instance. The second override accepts a Func<IServiceProvider, TStrategy>
factory method
for even more customization. The library internally decorates any IMultiTenantStrategy
with a wrapper providing basic
logging and exception handling.
// Register a custom strategy with the templated method.
services.AddMultiTenant<TenantInfo>()
.WithStrategy<MyStrategy>(myParam1, myParam2)...
// Or register a custom strategy with the overload method accepting a factory method.
// Note that the type parameter for WithStrategy is inferred by the compiler.
services.AddMultiTenant<TenantInfo>()
.WithStrategy(sp => new MyStrategy())...
MultiTenant strategies are registered in the dependency injection system under the
IMultiTenantStrategy
service type.
If multiple strategies are registered a specific one can be retrieving an
IEnumerable<IMultiTenantStrategy>
and filtering to the specific implementation type:
// Assume we have a service provider. The IEnumerable could be injected via
// other DI means as well.
var strategy = serviceProvider.GetService<IEnumerable<IMultiTenantStrategy>>
.Where(s => s.ImplementationType == typeof(StaticStrategy))
.SingleOrDefault();
Multiple strategies can be registered after AddMultiTenant<T>
and each strategy will be tried in the order configured
until a non-null identifier is returned. The remaining strategies are skipped for that request.
Note that some strategies are registered as singleton service so registering them multiple times
after AddMultiTenant<T>
is not recommended. The main use for registering multiple strategies of the same type is using
several instances of the DelegateStrategy
utilizing distinct logic.
NuGet package: Finbuckle.MultiTenant
Always uses the same identifier to resolve the tenant. Often useful in testing or to resolve to a fallback or default tenant by registering the strategy last. This strategy is configured as a singleton.
Configure by calling WithStaticStrategy
after AddMultiTenant<T>
in the ConfigureServices
method of the Startup
class and passing in the identifier to use for tenant resolution:
services.AddMultiTenant<TenantInfo>()
.WithStaticStrategy("MyTenant")...
NuGet package: Finbuckle.MultiTenant
Uses a provided Func<object, Task<string>>
to determine the tenant. For example the lambda
function async context => "initech"
would use "initech" as the identifier when resolving the tenant for every request.
This strategy is good to use for testing or simple logic. This strategy is configured as transient and multiple
instances can be registered.
Configure by calling WithDelegateStrategy
after AddMultiTenant<T>
in the ConfigureServices
method of the Startup
class. A Func<object, Task<string>>
is passed in which will be used with each request to resolve the tenant. A lambda
or async lambda can be used as the parameter:
// Use the request query parameter "tenant" to get the tenantId:
services.AddMultiTenant<TenantInfo>()
.WithDelegateStrategy(async context =>
{
var httpContext = context as HttpContext;
if(httpContext == null)
return null;
httpContext.Request.Query.TryGetValue("tenant", out StringValues tenantIdParam);
return tenantIdParam.ToString();
})...
NuGet package: Finbuckle.MultiTenant.AspNetCore
Uses the base (i.e. first) path segment to determine the tenant. For example, a request to "https://www.example.com/initech" would use "initech" as the identifier when resolving the tenant. This strategy is configured as a singleton.
Configure by calling WithBasePathStrategy
after AddMultiTenant<T>
in the ConfigureServices
method of the Startup
class:
services.AddMultiTenant<TenantInfo>()
.WithBasePathStrategy()...
This strategy can also adjust the ASP.NET Core Request.PathBase
and Request.Path
variables so that subsequent
middleware checking Request.Path
do not see the tenant identifier segment and generated relative urls include the
tenant base path automatically. This can be useful in some scenarios where an application makes certain assumptions
about paths that you otherwise cannot work around.
For example, a request to https://mydomain.com/mytenant/mypath
by default has a Request.PathBase
of /
and
a Request.Path
of /mytenant/mypath
. Setting this option will adjust these values to /mytenant
and /mypath
respectively when a tenant is successfully resolved with the BasePathStrategy
.
services.AddMultiTenant<TenantInfo>()
.WithBasePathStrategy(options =>
{
// defaults to false for compatibility
options.RebaseAspNetCorePathBase = true;
})...
Be aware that relative links to static files will be impacted so css files and other static resources may need to
be referenced using absolute urls. Alternatively, you can place the UseStaticFiles()
middleware after
the UseMultiTenant()
middware in the app pipeline configuration.
NuGet package: Finbuckle.MultiTenant.AspNetCore
Uses a claim to determine the tenant identifier. By default the first claim value with type __tenant__
is used, but a
custom type name can also be used. This strategy uses the default authentication scheme, which is usually cookie based,
but does not go so far as to set HttpContext.User
. Thus the ASP.NET Core authentication middleware should still be
used as normal, and in most use cases should come after UseMultiTenant
when using ClaimsStrategy
. Due to how the
authentication middleware is implemented there is practically no performance penalty when used in conjunction with
the ClaimStrategy
.
Note that this strategy is does not work well with per-tenant cookie names since it must know the cookie name before the tenant is resolved.
Configure by calling WithClaimStrategy
after AddMultiTenant<T>
in the
ConfigureServices
method of the Startup
class. An overload to accept a custom claim type is also available:
// This will check for a claim type __tenant__
services.AddMultiTenant<TenantInfo>()
.WithClaimStrategy()...
// This will check for a custom claim type
services.AddMultiTenant<TenantInfo>()
.WithClaimStrategy("MyClaimType")...
NuGet package: Finbuckle.MultiTenant.AspNetCore
Uses the ASP.NET Core session to retrieve the tenant identifier. This strategy is configured as a singleton.
Configure by calling WithSessionStrategy
after AddMultiTenant<T>
in the ConfigureServices
method of the Startup
class. This will use a default session key named __tenant__
. An overload of `WithSessionStrategy can be used to
specify a different key name:
// Configure to use "__tenant__" as the session key,
services.AddMultiTenant<TenantInfo>()
.WithSessionStrategy()...
// Or configure to use "my-tenant-session-key" as the session key,
services.AddMultiTenant<TenantInfo>()
.WithSessionStrategy("my-tenant-session-key")...
Note that an app will have to configure session state accordingly and then actually set the session variable. A typical use case is to register the session strategy before a more expensive strategy. The expensive strategy can set the session value so that for subsequent requests resolve the tenant without invoking the expensive strategy.
NuGet package: Finbuckle.MultiTenant.AspNetCore
Uses the __tenant__
route parameter (or a specified route parameter) to determine the tenant. For example, a request
to "https://www.example.com/initech/home/" and a route configuration of {__tenant__}/{controller=Home}/{action=Index}
would use "initech" as the identifier when resolving the tenant. The __tenant__
parameter can be placed anywhere in
the route path configuration. This strategy is configured as a singleton.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Other services...
// Use the default route parameter name "__tenant__":
services.AddMultiTenant<TenantInfo>()
.WithRouteStrategy()...
// Alternatively set a different route parameter name of "MyTenantRouteParam":
// services.AddMultiTenant<TenantInfo>()
// .WithRouteStrategy("MyTenantRouteParam")...
// Other services...
services.AddControllersWithViews(); // /AddMvc() for ASP.NET Core 3.1
// Other services...
}
public void Configure(IAppBuilder app, ...)
{
// Other middleware...
app.UseRouting(); // Important!
// Other middleware...
app.UseMultiTenant();
// Other middleware...
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{__tenant__}/{controller=Home}/{action=Index}");
});
}
}
NuGet package: Finbuckle.MultiTenant.AspNetCore
Uses request's host value to determine the tenant. By default the first host segment is used. For example, a request
to "https://initech.example.com/abc123" would use "initech" as the identifier when resolving the tenant. This strategy
can be difficult to use in a development environment. Make sure the development system is configured properly to allow
subdomains on localhost
. This strategy is configured as a singleton.
The host strategy uses a template string which defines how the strategy will find the tenant identifier. The pattern specifies the location for the tenant identifier using "__tenant__" and can contain other valid domain characters. It can also use '?' and '*' characters to represent one or "zero or more" segments. For example:
__tenant__.*
is the default if no pattern is provided and selects the first (or only) domain segment as the tenant
identifier.*.__tenant__.?
selects the main domain (or second to last) as the tenant identifier.__tenant__.example.com
will always use the subdomain for the tenant identifier, but only if there are no prior
subdomains and the overall host ends with "example.com".*.__tenant__.?.?
is similar to the above example except it will select the first subdomain even if others exist and
doesn't require ".com".__tenant__
will use the entire host as the tenant identifier, as opposed
to a single segment.Configure by calling WithHostStrategy
after AddMultiTenant<T>
in the ConfigureServices
method of the Startup
class. A template pattern can be specified with the overloaded version:
// Use the default template "__tenant__.*":
services.AddMultiTenant<TenantInfo>()
.WithHostStrategy()...
// Set a template which selects the main domain segment (see 2nd example above):
services.AddMultiTenant<TenantInfo>()
.WithHostStrategy("*.__tenant__.?")...
NuGet package: Finbuckle.MultiTenant.AspNetCore
Uses an HTTP request header to determine the tenant identifier. By default the header with key __tenant__
is used, but
a custom key can also be used.
Configure by calling WithHeaderStrategy
after AddMultiTenant<T>
in the
ConfigureServices
method of the Startup
class. An overload to accept a custom claim type is also available:
// This will check for a claim type __tenant__
services.AddMultiTenant<TenantInfo>()
.WithHeaderStrategy()...
// This will check for a custom claim type
services.AddMultiTenant<TenantInfo>()
.WithHeaderStrategy("MyHeaderKey")...
NuGet package: Finbuckle.MultiTenant.AspNetCore
This is a special strategy used for per-tenant authentication when remote authentication such as OpenID Connect or OAuth (e.g. Log in via Facebook) are used. This strategy is configured as a singleton.
The strategy is configured internally when WithPerTenantAuthentication
is called to
configure per-tenant authentication.