Mellon-MultiTenant 1.0.18

There is a newer version of this package available.
See the version list below for details.
dotnet add package Mellon-MultiTenant --version 1.0.18                
NuGet\Install-Package Mellon-MultiTenant -Version 1.0.18                
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="Mellon-MultiTenant" Version="1.0.18" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Mellon-MultiTenant --version 1.0.18                
#r "nuget: Mellon-MultiTenant, 1.0.18"                
#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 Mellon-MultiTenant as a Cake Addin
#addin nuget:?package=Mellon-MultiTenant&version=1.0.18

// Install Mellon-MultiTenant as a Cake Tool
#tool nuget:?package=Mellon-MultiTenant&version=1.0.18                

Contributors Forks Stargazers Issues LinkedIn LinkedIn

Mellon.MultiTenant by @PubDev

Package Version Alpha
Mellon-MultiTenant Nuget Nuget
Mellon-MultiTenant-ConfigServer Nuget Nuget
Mellon-MultiTenant-Azure Nuget Nuget

GitHublicense CI

Why Mellon, mellon is the Sindarin (and Noldorin) word for "friend", yes I'm a big fan of LoR, so let's be friends?

About The Project

This library was created to supply a set of tools to enable the creation of multi-tenant applications using .net.

Built With

Getting Started

Installation

With package Manager:

Install-Package Mellon-MultiTenant

With .NET CLI:

dotnet add package Mellon-MultiTenant

Configurations

There are two ways to configure the settings, via config and through the api

Settings
"MultiTenant": {
    "ApplicationName": "customer-api",
    "HttpHeaderKey": "x-tenant-name",
    "CookieKey": "tenant-name",
    "QueryStringKey": "tenant-name",
    "TenantSource": "AppSettings",
    "Tenants": [
      "client-a",
      "client-b",
      "client-c"
    ]
}
Property Description Default
ApplicationName Application name IHostEnvironment.ApplicationName
HttpHeaderKey Http Header key, where the tenant name will be passed null
CookieKey Http Cookie key, where the tenant name will be passed null
QueryStringKey Http Query String key, where the tenant name will be passed null
TenantSource Where the list of possible tenants will be stored, it can be from two sources: Settings or EnvironmentVariables Settings
Tenants When the property TenantSource is set to Settings this property must contain the list of tenants null
WithDefaultTenant When the tenant is not defined by the caller the lib will set the tenant as the tenant defined within this property, use it just when actually needed ๐Ÿ˜‰๐Ÿ‘ null

When TenantSource is set to EnvironmentVariables it will get the tenant list from the environment variable MULTITENANT_TENANTS, this environment variable must contain the list of possible tenants in a single string, separating the tenants using , for example:

$Env:MULTITENANT_TENANTS = 'client-a,client-b,client-c'

Using the API

You can also set the settings using these options while you are adding the services

builder.Services
        .AddMultiTenant(options =>
            options
                .WithApplicationName("customer-api")
                .WithHttpHeader("x-tenant-name")
                .WithCookie("tenant-name")
                .WithQueryString("tenant-name")
                .WithDefaultTenant("client-a")
                .LoadFromAppSettings()
        );
WithApplicationName(string)
  • Set the application name
WithHttpHeader(string)
  • Set the Http Header key, where the tenant name will be passed
WithCookie(string)
  • Set the Http Cookie key, where the tenant name will be passed
WithQueryString(string)
  • Set the Http Query String key, where the tenant name will be passed
WithDefaultTenant(string)
  • Set for when the tenant is not defined by the caller the lib will set the tenant as the tenant defined within this property, use it just when actually needed ๐Ÿ˜‰๐Ÿ‘
LoadFromSettings
  • Set for when the tenant list will be loaded from the settings MultiTenant:Tenants
LoadFromEnvironmentVariable
  • Set for when the tenant list will be loaded from the environment variable MULTITENANT_TENANTS
WithHttpContextLoad(Func<HttpContext, string> func)
  • When all the possibilities above does not meet with your need you can create a custom "Middleware" to idenfity the tenant based in a HttpContext
WithCustonTenantConfigurationSource<T>()
  • T must be an implementation of the interface ITenantConfigurationSourceWhen use it to define new a source of configurations for the tenants, for example, if the tenant settings are stored on xml files you could create something like this:
public class LocalXmlTenantSource : ITenantConfigurationSource
{
    private readonly IHostEnvironment _hostEnvironment;

    public LocalTenantSource(
        IHostEnvironment hostEnvironment)
    {
        _hostEnvironment = hostEnvironment;
    }

    public IConfigurationBuilder AddSource(
        string tenant,
        IConfigurationBuilder builder)
    {
        builder.AddXmlFile($"appsettings.{tenant}.xml", true);
        builder.AddJsonFile($"appsettings.{tenant}.{_hostEnvironment.EnvironmentName}.xml", true);

        return builder;
    }
}

Local

This is the default source of settings for the tenants, there is no need to enable it, it will search for the settings on the application following this pattern:

  • appsettings.{tenant}.json
  • appsettings.{_hostEnvironment.EnvironmentName}.json

Its also worth to mention that the configurations will also contain:

  • appsettings.json
  • appsettings.[environment].json
  • environment variables

Spring Cloud Config

You can also load the settings from a Spring Cloud Config Server!

To enable the usage you need to install an extra package:

With package Manager:

Install-Package Mellon-MultiTenant-ConfigServer

With .NET CLI:

dotnet add package Mellon-MultiTenant-ConfigServer

Once the package is installed you need to condifigure it services

builder.Services
        .AddMultiTenant()
        .AddMultiTenantSpringCloudConfig();

To setup Spring Cloud Config on your environment check this reporitory DotNet-ConfigServer

The application name for spring cloud config will be based on the settings MultiTenant.ApplicationName and the label will be tenant name.

Example: customer-api-client-a.yaml

being:

  • customer-api the application name
  • client-a the tenant name

More over its worthy to mention that the settings for each customer will also have the settings of the current files:

  • appsettings.json
  • appsettings.[environment].json
  • environment variables

Azure App Configuration

You can also use as source of configuration Azure App Configuration

With package Manager:

Install-Package Mellon-MultiTenant-Azure

With .NET CLI:

dotnet add package Mellon-MultiTenant-Azure

Once the package is installed you need to condifigure it services

builder.Services
        .AddMultiTenant()
        .AddMultiTenantAzureAppConfiguration();

AddMultiTenantAzureAppConfiguration(Action<AzureMultiTenantOptions> action = null)

if the action is not passed, the connection string used to connect on azure will loaded from AzureAppConfigurationConnectionString

if you want to elaborate more, how you are going to connect on azure, you can use the AzureMultiTenantOptions, there is a property, which is a Func<IServiceProvider, string, Action<AzureAppConfigurationOptions>>, where the first parameter is the ServiceProvider, where you can extract the services; a string, being the tenant name; and the return of this Func must be an Action<AzureAppConfigurationOptions>. For example:

builder.Services
        .AddMultiTenant()
        .AddMultiTenantAzureAppConfiguration(options =>
            options.AzureAppConfigurationOptions = (serviceProvider, tenant) =>
            {
                var configuration = serviceProvider.GetRequiredService<IConfiguration>();

                return azureOptions => azureOptions
                    .Connect(configuration["AzureAppConfigurationConnectionString"])
                    .Select("*", tenant);
            }
        );

Usage / Samples

You can find some examples of how to use this library on the folder /samples

Web API

To enable it on your api you first need to add the services:

builder.Services.AddMultiTenant();

then you need also to register the middleware used to identify the tenant based on the HttpRequests

app.UseMultiTenant();

Once that is done you will be able to use the interface IMultiTenantConfiguration, this interface will bahave exactly as the IConfiguration interface, but containing only the current tenant settings:

Example:

app.MapGet("/", (IMultiTenantConfiguration configuration) =>
{
    return new
    {
        Tenant = configuration.Tenant,
        Message = configuration["Message"],
    };
});

EF Core Migrations:

To use it with EF Core is quite simple, you need to use the interface IMultiTenantConfiguration as mention above to setup your EF Context

Setup
builder.Services.AddDbContext<DataBaseContext>(
    (IServiceProvider serviceProvider, DbContextOptionsBuilder options) =>
    {
        var configuration = serviceProvider.GetRequiredService<IMultiTenantConfiguration>();

        options.UseSqlServer(configuration?["ConnectionStrings:DefaultConnection"]);
    });
Migrations

To apply the migrations, you only need to do this:

var tenants = app.Services.GetRequiredService<MultiTenantSettings>();

foreach (var tenant in tenants.Tenants)
{
    using (var scope = app.Services.CreateScope())
    {
        var tenantSettings = scope.ServiceProvider.GetRequiredService<TenantSettings>();

        tenantSettings.SetCurrentTenant(tenant);

        var db = scope.ServiceProvider.GetRequiredService<DataBaseContext>();

        await db.Database.MigrateAsync();
    }
}

app.Run();

Extras

We know that settings can be changed all the time, but in order to get our applications running on with the latest settings we need to restart the application, it caused downtime and its not very practical. Keeping that in mind, we added also an endpoint that when called will refresh all the settings for the all the tenants or for a specific tenant:

  • /refresh-settings
  • /refresh-settings/{tenantName}

PS: this will work only with AzureAppConfiguration and SpringCloudConfig

Roadmap

  • Add unit tests ๐Ÿงช
  • Add new Config Source
  • Load the Tenants from a web-api request
  • Enable the usage with HanfFire

See the open issues for a full list of proposed features (and known issues).

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Contact

  • Humberto Rodrigues - @1bberto - humberto_henrique1@live.com
  • Rafael Nagai - @naganaga - rafakenji23@gmail.com

Project Link: https://github.com/1bberto/Mellon.MultiTenant

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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.

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
3.0.51-alpha 75 10/2/2024
3.0.36 97 10/2/2024
2.0.49-alpha 205 11/26/2023
2.0.35 10,492 11/26/2023
1.1.48-alpha 126 10/23/2023
1.1.46-alpha 138 8/1/2023
1.1.45-alpha 135 7/8/2023
1.1.44-alpha 117 6/20/2023
1.1.43-alpha 133 4/4/2023
1.1.42-alpha 109 4/4/2023
1.1.41-alpha 145 3/25/2023
1.1.34 3,914 10/23/2023
1.1.33 4,228 8/1/2023
1.1.32 540 7/8/2023
1.1.31 1,010 6/20/2023
1.1.30 772 4/4/2023
1.1.29 623 3/25/2023
1.0.40-alpha 136 2/25/2023
1.0.39-alpha 131 2/25/2023
1.0.38-alpha 134 2/25/2023
1.0.37-alpha 153 2/25/2023
1.0.36-alpha 127 2/24/2023
1.0.34-alpha 146 2/17/2023
1.0.33-alpha 160 1/6/2023
1.0.30-alpha 150 1/4/2023
1.0.29-alpha 144 1/4/2023
1.0.28 1,021 2/25/2023
1.0.28-alpha 141 1/4/2023
1.0.27 231 2/25/2023
1.0.27-alpha 144 1/2/2023
1.0.26 308 2/24/2023
1.0.26-alpha 134 1/2/2023
1.0.25 233 2/24/2023
1.0.25-alpha 156 1/2/2023
1.0.24 289 2/17/2023
1.0.24-alpha 152 1/2/2023
1.0.23 288 1/6/2023
1.0.23-alpha 140 1/2/2023
1.0.22 295 1/4/2023
1.0.22-alpha 154 1/2/2023
1.0.21 285 1/3/2023
1.0.20 275 1/3/2023
1.0.19 279 1/3/2023
1.0.18 276 1/2/2023
1.0.17 296 1/2/2023
1.0.17-alpha 146 1/2/2023
1.0.16 309 1/2/2023
1.0.16-alpha 143 1/2/2023
1.0.15 291 1/2/2023
1.0.14-alpha 156 1/2/2023
1.0.13 282 12/30/2022
1.0.12 257 12/29/2022
1.0.11 273 12/29/2022
1.0.11-alpha 123 12/30/2022
1.0.10 280 12/29/2022
1.0.10-alpha 138 12/30/2022
1.0.9 290 12/29/2022
1.0.8 277 12/29/2022
1.0.7 274 12/29/2022
1.0.6 275 12/29/2022
1.0.4 314 12/27/2022
1.0.4-alpha 163 12/29/2022
1.0.3-alpha 155 12/29/2022