Finbuckle.MultiTenant

repository·main·Indexed 23 days ago

https://github.com/finbuckle/finbuckle.multitenant

An open source multi-tenancy library for modern .NET applications providing tools for tenant resolution, per-tenant configuration, and data isolation. It supports various identification strategies (Host, Route, Base Path, Header, Claim, Session, and HttpContext) and integrates with ASP.NET Core, Entity Framework Core, and ASP.NET Core Identity to ensure separated application behavior and data isolation between tenants.

Tokens
22.2K
Snippets
45
Records
84
Agent score
79%

What's inside Finbuckle.MultiTenant

  1. Overview of Finbuckle.MultiTenant

    main

    Finbuckle.MultiTenant is an open source multi-tenancy library for modern .NET. It is designed to enable three core multi-tenant capabilities:

    1. Tenant Resolution: Identifying which tenant a request belongs to.
    2. Per-tenant app behavior: Configuring application settings and options differently for each tenant.
    3. Per-tenant data isolation: Ensuring data is separated and isolated between tenants.

    Starting with version 10, major version releases of the library align with major .NET version releases. For example, version 10 supports .NET 10. It is recommended to target the version of MultiTenant that matches your .NET version.

  2. What is MultiTenant?

    main

    MultiTenant is an open source multi-tenancy library for modern .NET. It provides capabilities for:

    • Tenant resolution: Identifying which tenant a request belongs to.
    • Per-tenant app behavior: Customizing how the application behaves based on the active tenant.
    • Per-tenant data isolation: Ensuring data is separated and isolated between different tenants.

    Version Compatibility Note: Starting with v10, major version releases of MultiTenant align with major .NET releases. You should generally target the version of MultiTenant that matches your .NET version. This release (v10.1.2) supports .NET 10.

  3. Understand the Web API Sample architecture

    main

    This sample demonstrates how to implement multi-tenancy in an ASP.NET Core Web API using Finbuckle.MultiTenant. It uses a Base Path Strategy where the tenant is identified by the first segment of the URL path (e.g., /tenant-id/endpoint).

    Key architectural features include:

    • Path Rebasing: The BasePathStrategy automatically adjusts the ASP.NET Core path base. This allows your API endpoints to be defined without the tenant identifier in the route template (e.g., you define /weatherforecast instead of /{tenant}/weatherforecast).
    • Custom Tenant Data: The sample demonstrates extending the standard TenantInfo class with custom properties, such as PreferredLanguage, to drive tenant-specific logic.
  4. Explore the Identity App Sample

    main

    The Identity App Sample is a reference implementation of a multi-tenant ASP.NET Core MVC application. It demonstrates how to integrate Finbuckle.MultiTenant with ASP.NET Core Identity to achieve per-tenant authentication and user management.

    Key architectural patterns demonstrated include:

    • Tenant Identification: Uses a route strategy where tenants are identified by a __tenant__ route parameter (e.g., /acme/Home/Index).
    • Configuration: Loads tenant definitions from appsettings.json.
    • Data Isolation: Implements per-tenant database isolation using Entity Framework Core and the MultiTenantDbContext pattern.
    • Identity Integration: Uses MultiTenantPageRouteModelConvention to ensure Razor Pages (Identity UI) automatically include tenant routing.
    • Custom Metadata: Demonstrates extending the TenantInfo class with custom properties (e.g., a Tier property).
  5. What are MultiTenant Stores and how do they work?

    main

    A MultiTenant store is responsible for retrieving information about a tenant based on an identifier string determined by a MultiTenant strategy. The retrieved information is used to create an ITenantInfo object, which provides the current tenant information to your application.

    MultiTenant provides several built-in stores, but you can also implement custom stores by implementing the IMultiTenantStore<TTenantInfo> interface. The type parameter TTenantInfo must match the type passed to AddMultiTenant<TTenantInfo> at compile time.

  6. Use multiple MultiTenant stores

    main

    You can register multiple stores. When a strategy returns a non-null identifier, the stores are checked in the order they were registered until a matching tenant is resolved.

    Note: If you use multiple strategies, a single store might be checked multiple times during the tenant resolution process.

  7. Access the current tenant via `MultiTenantContext<TTenantInfo>`

    main

    The MultiTenantContext<TTenantInfo> object holds all information regarding the currently resolved tenant for a request. It implements IMultiTenantContext and IMultiTenantContext<TTenantInfo>.

    Key properties:

    • TenantInfo: The resolved tenant information.
    • StrategyInfo: Details on which strategy was used to determine the tenant.
    • StoreInfo: Details on where the tenant information was retrieved from.
    • IsResolved: A boolean indicating if a tenant was successfully identified.

    How to access it:

    • Dependency Injection: Access it via IMultiTenantContextAccessor.
    • ASP.NET Core: Call the GetMultiTenantContext<TTenantInfo>() extension method on the current request's HttpContext object.
    • Manual Setting: Use the HttpContext.SetTenantInfo extension method to manually set the current tenant (though this is typically handled by the middleware).
  8. Configure Ambient Route Value Promotion for the Route Strategy

    main

    When using WithRouteStrategy, you can enable or disable Ambient Route Value Promotion.

    When useTenantAmbientRouteValue is set to true, Finbuckle wraps ASP.NET Core's LinkGenerator. This ensures that links created by MVC, Razor Pages, minimal APIs, and tag helpers automatically include the tenant route value as an explicit part of the URL, even if it was only present as an ambient value in the current request.

    Set this to true if you want generated URLs to consistently include the tenant segment. Set it to false if you prefer standard ASP.NET Core behavior and want to manage tenant route values manually.

    builder.Services.AddMultiTenant<TenantInfo>()
        .WithRouteStrategy("tenant", useTenantAmbientRouteValue: true)
        .WithConfigurationStore();
  9. Isolate Identity Authentication per Tenant

    main
    ASP.NET Core Identity uses cookies for authentication. MultiTenant can isolate Identity authentication so that user sessions are unique per tenant. This allows a user to potentially have different sessions or authentication states depending on the tenant context. To customize this, refer to the [per-tenant authentication] documentation to configure authentication options per tenant.
  10. Querying and Filtering Multi-Tenant Data

    main

    By default, EF Core queries are automatically filtered to only return results associated with the current TenantInfo.

    Bypassing the Tenant Filter

    If you need to query across all tenants (e.g., for administrative tasks), use the IgnoreQueryFilters method. You must pass the specific tenant token constant used by Finbuckle: Finbuckle.MultiTenant.Abstractions.Constants.TenantToken.

    Note on Limitations: The global query filter is applied only at the root level of a query. Entities loaded via Include or ThenInclude are not filtered by default, but if all involved entity classes have the [MultiTenant] attribute, results will still be associated with the same tenant.

    // TenantBlogs will contain all blogs, regardless of tenant.
    var myTenantInfo = ...;
    var db = MultiTenantDbContext.Create<BloggingDbContext, TenantInfo>(myTenantInfo);
    var tenantBlogs = db.Blogs
        .IgnoreQueryFilters(Finbuckle.MultiTenant.Abstractions.Constants.TenantToken)
        .ToList();
  11. How tenant resolution works

    main

    Tenant resolution is performed by the TenantResolver class using configured strategies and stores.

    The Resolution Process:

    1. The resolver tries strategies in the order they were added (though static and per-tenant authentication strategies have lower priority).
    2. When a strategy returns a tenant identifier, the resolver queries the configured stores in registration order.
    3. The first store to return a TenantInfo determines the resolved tenant.
    4. If no store returns a TenantInfo, the resolver moves to the next strategy.

    Customizing Resolution via TenantResolver Options: Options are configured within the AddMultiTenant<TTenantInfo> method:

    • IgnoredIdentifiers: A list of tenant identifiers that should be ignored by the resolver.
    • Events: Hooks to intercept the resolution process:
      • OnStrategyResolveCompleted: Called after each strategy attempt. Use IdentifierFound to check success and Identifier to override the result.
      • OnStoreResolveCompleted: Called after each store attempt. Use TenantFound to check success and TenantInfo to override the result. Providing a non-null TenantInfo stops further searches.
      • OnTenantResolveCompleted: Called once after a tenant is resolved. Use MultiTenantContext to override the final result.
  12. Use Base Path Strategy for tenant identification

    main

    In this sample, tenants are identified via the URL path. The first segment of the path is treated as the tenant identifier. Because the BasePathStrategy is used, the application automatically handles the path rebasing so that the underlying ASP.NET Core routing works as if the tenant segment were not there.

    Example URL patterns:

    • /acme/weatherforecast identifies the acme tenant.
    • /parisian/weatherforecast identifies the parisian tenant.