{"id":32491300,"url":"https://github.com/pub-dev/mellon.multitenant","last_synced_at":"2025-10-27T09:20:48.454Z","repository":{"id":65128880,"uuid":"582488822","full_name":"Pub-Dev/Mellon.MultiTenant","owner":"Pub-Dev","description":"A library created to help with multi-tenant applications made in net","archived":false,"fork":false,"pushed_at":"2025-02-10T20:03:17.000Z","size":156,"stargazers_count":41,"open_issues_count":3,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-10-26T21:13:08.476Z","etag":null,"topics":["azure-app-configuration","configuration-loader","dotnet","dotnet-core","hangfire","hangfire-dotnet-core","hangfire-extension","hangfire-multitenant","multi-tenant","multi-tenant-applications","multitenant","pub-dev","pubdev","scharp","spring-cloud-config-dotnet"],"latest_commit_sha":null,"homepage":"https://pub-dev.github.io/Mellon.MultiTenant","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Pub-Dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-27T02:02:22.000Z","updated_at":"2025-08-22T05:43:10.000Z","dependencies_parsed_at":"2023-02-06T09:47:04.456Z","dependency_job_id":"4f0d1b1c-bff0-4abe-8cb1-d1a003210a20","html_url":"https://github.com/Pub-Dev/Mellon.MultiTenant","commit_stats":null,"previous_names":["1bberto/mellon.multitenant"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/Pub-Dev/Mellon.MultiTenant","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pub-Dev%2FMellon.MultiTenant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pub-Dev%2FMellon.MultiTenant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pub-Dev%2FMellon.MultiTenant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pub-Dev%2FMellon.MultiTenant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Pub-Dev","download_url":"https://codeload.github.com/Pub-Dev/Mellon.MultiTenant/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pub-Dev%2FMellon.MultiTenant/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281244058,"owners_count":26467812,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-27T02:00:05.855Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure-app-configuration","configuration-loader","dotnet","dotnet-core","hangfire","hangfire-dotnet-core","hangfire-extension","hangfire-multitenant","multi-tenant","multi-tenant-applications","multitenant","pub-dev","pubdev","scharp","spring-cloud-config-dotnet"],"created_at":"2025-10-27T09:20:44.087Z","updated_at":"2025-10-27T09:20:48.440Z","avatar_url":"https://github.com/Pub-Dev.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Contributors][contributors-shield]][contributors-url] [![Forks][forks-shield]][forks-url] [![Stargazers][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] [![LinkedIn][linkedin-shield]][linkedin-url] [![LinkedIn][linkedin-shield]][linkedin2-url]\n\n## Mellon.MultiTenant by [@PubDev](https://www.youtube.com/@PubDev)\n\n|               Package               |                                                                  Version                                                                   |                                                                     Alpha                                                                     |\n| :---------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------: |\n|       **Mellon.MultiTenant**        |              [![Nuget](https://img.shields.io/nuget/v/Mellon-MultiTenant)](https://www.nuget.org/packages/Mellon-MultiTenant)              |              [![Nuget](https://img.shields.io/nuget/vpre/Mellon-MultiTenant)](https://www.nuget.org/packages/Mellon-MultiTenant)              |\n|     **Mellon.MultiTenant.Base**     |         [![Nuget](https://img.shields.io/nuget/v/Mellon-MultiTenant-Base)](https://www.nuget.org/packages/Mellon-MultiTenant-Base)         |         [![Nuget](https://img.shields.io/nuget/vpre/Mellon-MultiTenant-Base)](https://www.nuget.org/packages/Mellon-MultiTenant-Base)         |\n| **Mellon.MultiTenant.ConfigServer** | [![Nuget](https://img.shields.io/nuget/v/Mellon-MultiTenant-ConfigServer)](https://www.nuget.org/packages/Mellon-MultiTenant-ConfigServer) | [![Nuget](https://img.shields.io/nuget/vpre/Mellon-MultiTenant-ConfigServer)](https://www.nuget.org/packages/Mellon-MultiTenant-ConfigServer) |\n|    **Mellon.MultiTenant.Azure**     |        [![Nuget](https://img.shields.io/nuget/v/Mellon-MultiTenant-Azure)](https://www.nuget.org/packages/Mellon-MultiTenant-Azure)        |        [![Nuget](https://img.shields.io/nuget/vpre/Mellon-MultiTenant-Azure)](https://www.nuget.org/packages/Mellon-MultiTenant-Azure)        |\n|   **Mellon.MultiTenant.Hangfire**   |     [![Nuget](https://img.shields.io/nuget/v/Mellon-MultiTenant-Hangfire)](https://www.nuget.org/packages/Mellon-MultiTenant-Hangfire)     |     [![Nuget](https://img.shields.io/nuget/vpre/Mellon-MultiTenant-Hangfire)](https://www.nuget.org/packages/Mellon-MultiTenant-Hangfire)     |\n\n![Downloads](https://img.shields.io/nuget/dt/Mellon-MultiTenant.svg 'Downloads') [![GitHublicense](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/1bberto/Mellon.MultiTenant/main/LICENSE) [![CI](https://github.com/Pub-Dev/Mellon.MultiTenant/actions/workflows/buildAndPush.yml/badge.svg?branch=main)](https://github.com/Pub-Dev/Mellon.MultiTenant/actions/workflows/buildAndPush.yml)\n\nWhy Mellon, mellon is the Sindarin (and Noldorin) word for \"friend\", yes I'm a big fan of LoR, so let's be friends?\n\n## About The Project\n\nThis library was created to supply a set of tools to enable the creation of multi-tenant applications using .net.\n\n### Built With\n\n- [net8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)\n- [steeltoe](https://docs.steeltoe.io/api/v3/configuration)\n- [Azure App Configuration](https://learn.microsoft.com/en-us/azure/azure-app-configuration/overview)\n- [Spring Cloud Config](https://cloud.spring.io/spring-cloud-config/reference/html)\n- The most important, Love ❤️\n\n## Getting Started\n\n## Installation\n\nWith package Manager:\n\n```\nInstall-Package Mellon.MultiTenant\n```\n\nWith .NET CLI:\n\n```\ndotnet add package Mellon.MultiTenant\n```\n\n### Configurations\n\nThere are two ways to configure the settings, via config and through the api\n\n#### Settings\n\n```json\n\"MultiTenant\": {\n    \"ApplicationName\": \"customer-api\",\n    \"HttpHeaderKey\": \"x-tenant-name\",\n    \"CookieKey\": \"tenant-name\",\n    \"QueryStringKey\": \"tenant-name\",\n    \"TenantSource\": \"Settings\",\n    \"SkipTenantCheckPaths\": [\"^/swagger.*\"],\n    \"Tenants\": [\n      \"client-a\",\n      \"client-b\",\n      \"client-c\"\n    ]\n}\n```\n\n|       Property       |                                                                        Description                                                                         |             Default              |\n| :------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------: |\n|   ApplicationName    |                                                                      Application name                                                                      | IHostEnvironment.ApplicationName |\n|    HttpHeaderKey     |                                                   HTTP Header key, where the tenant name will be passed                                                    |              `null`              |\n|      CookieKey       |                                                   HTTP Cookie key, where the tenant name will be passed                                                    |              `null`              |\n|    QueryStringKey    |                                                HTTP Query String key, where the tenant name will be passed                                                 |              `null`              |\n|     TenantSource     |                    Where the list of possible tenants will be stored, it can be from two sources: `Settings` or `EnvironmentVariables`                     |            `Settings`            |\n|       Tenants        |                            When the property `TenantSource` is set to `Settings` this property must contain the list of tenants                            |              `null`              |\n|  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`              |\n| SkipTenantCheckPaths |             Endpoints which the tenant do not need to be identified, for example: Swagger endpoints, you can use a regex string `^/swagger.*`              |              `null`              |\n\nWhen `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 `,`\nfor example:\n\n```\n$Env:MULTITENANT_TENANTS = 'client-a,client-b,client-c'\n```\n\n### Using the API\n\nYou can also set the settings using these options while you are adding the services\n\n```csharp\nbuilder.Services\n        .AddMultiTenant(options =\u003e\n            options\n                .WithApplicationName(\"customer-api\")\n                .WithHttpHeader(\"x-tenant-name\")\n                .WithCookie(\"tenant-name\")\n                .WithQueryString(\"tenant-name\")\n                .WithDefaultTenant(\"client-a\")\n                .LoadFromSettings()\n        );\n```\n\n#### `WithApplicationName(string)`\n\n- Set the application name\n\n#### `WithHttpHeader(string)`\n\n- Set the HTTP Header key, where the tenant name will be passed\n\n#### `WithCookie(string)`\n\n- Set the HTTP Cookie key, where the tenant name will be passed\n\n#### `WithQueryString(string)`\n\n- Set the HTTP Query String key, where the tenant name will be passed\n\n#### `WithDefaultTenant(string)`\n\n- 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 needed 😉👍\n\n### `WithSkipTenantCheckPaths(string)`\n\n- Add a path that will be skipped during the tenant identification\n\n### `WithSkipTenantCheckPaths(params string[])`\n\n- Add paths that will be skipped during the tenant identification\n\n#### `LoadFromSettings`\n\n- Set for when the tenant list will be loaded from the settings **MultiTenant:Tenants**\n\n#### `LoadFromEnvironmentVariable`\n\n- Set for when the tenant list will be loaded from the environment variable **MULTITENANT_TENANTS**\n\n#### `LoadFromEndpoint(Func\u003cEndpointSettings, IConfiguration, string[]\u003e func)` and `LoadFromEndpoint\u003cT\u003e(Func\u003cT, string\u003e func)`\n\n- Define a Func or pass a type to define how the tenant list will be loaded from a http endpoint, to make it work you need to pass a new set of properties on the app settings\n\n```json\n\"MultiTenant\": {\n    // other settings...\n    \"Endpoint\": {\n      \"Url\": \"[endpoint]\",\n      \"Method\": \"GET\",\n      \"Authorization\": \"Basic $user $password\"\n    },\n    // other settings...\n}\n```\n\nIf the endpoint has authorization you can set the credencials on the property `Authorization`\n\nYou can pass the Func and do what you see fit when getting the list of tenants\n\nExample:\n\n```csharp\nservices\n    .AddMultiTenant(options =\u003e options.LoadFromEndpoint((endpointOptions, configuration) =\u003e\n        {\n            var request = new HttpRequestMessage()\n            {\n                RequestUri = new Uri(endpointOptions.Url),\n                Method = new HttpMethod(endpointOptions.Method),\n            };\n\n            if (!string.IsNullOrEmpty(endpointOptions.Authorization))\n            {\n                request.Headers.Add(\"Authorization\", endpointOptions.Authorization);\n            }\n\n            using (var client = new HttpClient())\n            {\n                var result = client.Send(request);\n\n                if (result.IsSuccessStatusCode)\n                {\n                    var data = result.Content.ReadFromJsonAsync\u003cIEnumerable\u003cTenant\u003e\u003e().GetAwaiter().GetResult();\n\n                    return data!.Select(x =\u003e x.Id).ToArray();\n                }\n                else\n                {\n                    var statusCode = result.StatusCode;\n\n                    var reason = result.ReasonPhrase;\n\n                    var content = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();\n\n                    throw new Exception($@\"Error to load tenants from the url {endpointOptions.Url} StatusCode: {statusCode} Reason: {reason} Content: {content}\");\n                }\n            }\n        }));\n```\n\nOr if your use case does not require customization you can just call the other method, Which behind the scene does basically a http request to the endpoint set on the `Endpoint` settings, respecting the `Url`, `Method` and `Authorization`\n\nExample:\n\n```csharp\nservices\n    .AddMultiTenant(options =\u003e options.LoadFromEndpoint\u003cTenant\u003e(x =\u003e x.TenantId));\n```\n\n#### `WithHttpContextLoad(Func\u003cHttpContext, string\u003e func)`\n\n- When all the possibilities above do not meet your need you can create a custom \"Middleware\" to identify the tenant based on a `HttpContext`\n\n#### `WithCustomTenantConfigurationSource\u003cT\u003e()`\n\n- `T` must be an implementation of the interface `ITenantConfigurationSource` 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:\n\n```csharp\npublic class LocalXmlTenantSource : ITenantConfigurationSource\n{\n    private readonly IHostEnvironment _hostEnvironment;\n\n    public LocalTenantSource(\n        IHostEnvironment hostEnvironment)\n    {\n        _hostEnvironment = hostEnvironment;\n    }\n\n    public IConfigurationBuilder AddSource(\n        string tenant,\n        IConfigurationBuilder builder)\n    {\n        builder.AddXmlFile($\"appsettings.{tenant}.xml\", true);\n        builder.AddXmlFile($\"appsettings.{tenant}.{_hostEnvironment.EnvironmentName}.xml\", true);\n\n        return builder;\n    }\n}\n```\n\n### Local\n\nThis 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:\n\n- `appsettings.{tenant}.json`\n- `appsettings.{tenant}.{_hostEnvironment.EnvironmentName}.json`\n\nIt is also worth mentioning that the configurations will also contain:\n\n- `appsettings.json`\n- `appsettings.[environment].json`\n- `environment variables`\n\n### Spring Cloud Config\n\nYou can also load the settings from a **Spring Cloud Config Server**!\n\nTo enable the usage you need to install an extra package:\n\nWith package Manager:\n\n```\nInstall-Package Mellon.MultiTenant.ConfigServer\n```\n\nWith .NET CLI:\n\n```\ndotnet add package Mellon.MultiTenant.ConfigServer\n```\n\nOnce the package is installed you need to configure its services\n\n```csharp\nbuilder.Services\n        .AddMultiTenant()\n        .AddMultiTenantSpringCloudConfig();\n```\n\nTo setup [Spring Cloud Config](https://cloud.spring.io/spring-cloud-config/reference/html) on your environment check this reporitory [DotNet-ConfigServer](https://github.com/Pub-Dev/Lesson-DotNet-ConfigServer)\n\nThe application name for spring cloud config will be based on the settings _**MultiTenant.ApplicationName**_ and the label will be tenant name.\n\nExample:\n_customer-api-client-a.yaml_\n\nbeing:\n\n- _customer-api_ the application name\n- _client-a_ the tenant name\n\nMoreover, it is worth mentioning that the settings for each customer will also have the settings of the current files:\n\n- appsettings.json\n- appsettings.[environment].json\n- environment variables\n\n### Azure App Configuration\n\nYou can also use it as a source of configuration the **Azure App Configuration**\n\nWith package Manager:\n\n```\nInstall-Package Mellon.MultiTenant.Azure\n```\n\nWith .NET CLI:\n\n```\ndotnet add package Mellon.MultiTenant.Azure\n```\n\nOnce the package is installed you need to configure its services\n\n```csharp\nbuilder.Services\n        .AddMultiTenant()\n        .AddMultiTenantAzureAppConfiguration();\n```\n\n#### `AddMultiTenantAzureAppConfiguration(Action\u003cAzureMultiTenantOptions\u003e action = null)`\n\nif the action is not passed, the connection string used to connect on azure will be loaded from `AzureAppConfigurationConnectionString`\n\nif you want to elaborate more, on how you are going to connect on Azure, you can use the `AzureMultiTenantOptions`, there is a property, which is a `Func\u003cIServiceProvider, string, Action\u003cAzureAppConfigurationOptions\u003e\u003e`, 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\u003cAzureAppConfigurationOptions\u003e`.\nFor example:\n\n```csharp\nbuilder.Services\n        .AddMultiTenant()\n        .AddMultiTenantAzureAppConfiguration(options =\u003e\n            options.AzureAppConfigurationOptions = (serviceProvider, tenant) =\u003e\n            {\n                var configuration = serviceProvider.GetRequiredService\u003cIConfiguration\u003e();\n\n                return azureOptions =\u003e azureOptions\n                    .Connect(configuration[\"AzureAppConfigurationConnectionString\"])\n                    .Select(\"*\", tenant);\n            }\n        );\n```\n\n## Hangfire\n\nIf you need to work with jobs with the concept of multi-tenant using **Hangfire**\n\nTo enable the usage you need to install an extra package:\n\nWith package Manager:\n\n```\nInstall-Package Mellon.MultiTenant.Hangfire\n```\n\nWith .NET CLI:\n\n```\ndotnet add package Mellon.MultiTenant.Hangfire\n```\n\nOnce the package is installed you need to configure its services\n\n```csharp\nbuilder.Services\n        .AddMultiTenant()\n        .AddMultiTenantHangfire();\n```\n\nAnd when adding the Service `AddHangfire` you need to call the method `UseMultiTenant` passing the `IServiceProvider`\n\n```csharp\nbuilder.Services.AddHangfire((serviceProvider, config) =\u003e\n{\n    // some code\n    config.UseMultiTenant(serviceProvider);\n    // some code\n});\n```\n\nTo create the Worker, you need to pass the queues with the tenant names\n\n```csharp\nbuilder.Services.AddHangfireServer((serviceProvider, config) =\u003e\n{\n    var multiTenantSettings = serviceProvider.GetRequiredService\u003cMultiTenantSettings\u003e();\n\n    var queues = new List\u003cstring\u003e(multiTenantSettings.Tenants);\n\n    // if you want to add more queues\n    queues.Add(\"cron\");\n    queues.Add(\"default\");\n    config.Queues = tenants.ToArray();\n\n    // some code\n});\n```\n\nFor **ScheduleJobs** and **BackgroundJob** the queue name will be the _tenant name_\n\nFor **RecurringJobs** the default queue could vary from job to job\n\n### RecurringJobs 🗓️\n\nTo create Recurring Jobs you just need to use the interface `IMultiTenantRecurringJobManager`, this interface will have the following methods and extension methods:\n\n#### `AddOrUpdateForAllTenants\u003cT\u003e(string recurringJobId,Expression\u003cFunc\u003cT, Task\u003e\u003e methodCall, string cronExpression, TimeZoneInfo timeZone = null,string queue = \"default\")`\n\nIt will create a recurring job from the type `T.Method` for all the tenants\n\n#### `AddOrUpdateForAllTenants(string recurringJobId, Job job, string cronExpression, TimeZoneInfo timeZone)`\n\nIt will create a recurring job for all the tenants\n\n#### `AddOrUpdate\u003cT\u003e(string recurringJobId,Expression\u003cFunc\u003cT, Task\u003e\u003e methodCall, string cronExpression, TimeZoneInfo timeZone = null,string queue = \"default\")`\n\nIt will create a recurring job from the type `T.Method` for the current tenant\n\n#### `AddOrUpdate(string recurringJobId, Job job, string cronExpression, TimeZoneInfo timeZone)`\n\nIt will create a recurring job for the current tenant\n\n#### `RemoveIfExistsForAllTenants(string recurringJobId)`\n\nIt will remove the recurring job for all the tenants\n\n#### `RemoveIfExists(string recurringJobId)`\n\nIt will remove the recurring job for the current tenants\n\n#### `TriggerForAllTenants(string recurringJobId)`\n\nIt will enqueue the recurring job for all the tenants\n\n#### `Trigger(string recurringJobId)`\n\nIt will enqueue the recurring job for the current tenants\n\n### Background Jobs ⚙️\n\nTo create Background Jobs you just need to use the interface `IMultiTenantBackgroundJobManager`, this interface will have the following methods and extension methods:\n\n#### `EnqueueForAllTenants(Expression\u003cAction\u003e methodCall)`\n\nIt will enqueue a job execution for all the tenant, sending the jobs for a queue with the tenant's name, the return object with consist in a list containing the tenant and the JobId created for that tenant\n\n#### `EnqueueForAllTenants(Expression\u003cFunc\u003cTask\u003e\u003e methodCall)`\n\nIt will enqueue a job execution for all the tenant, sending the jobs for a queue with the tenant's name, the return object with consist in a list containing the tenant and the JobId created for that tenant\n\n#### `EnqueueForAllTenants\u003cT\u003e(Expression\u003cAction\u003cT\u003e\u003e methodCall)`\n\nIt will enqueue a job execution of type `T.Method` for all the tenant, sending the jobs for a queue with the tenant's name, the return object with consist in a list containing the tenant and the JobId created for that tenant\n\n#### `EnqueueForAllTenants\u003cT\u003e(Expression\u003cFunc\u003cT, Task\u003e\u003e methodCall)`\n\nIt will enqueue a job execution of type `T.Task\u003cMethod\u003e` for all the tenant, sending the jobs for a queue with the tenant's name, the return object with consist in a list containing the tenant and the JobId created for that tenant\n\n#### `Enqueue(Expression\u003cAction\u003e methodCall)`\n\nIt will enqueue a job execution for the current tenant, sending the job for a queue with the tenant's name, the return the jobId\n\n#### `Enqueue(Expression\u003cFunc\u003cTask\u003e\u003e methodCall)`\n\nIt will enqueue a job execution for the current tenant, sending the job for a queue with the tenant's name, the return the jobId\n\n#### `Enqueue\u003cT\u003e(Expression\u003cAction\u003cT\u003e\u003e methodCall)`\n\nIt will enqueue a job execution of type `T.Method` for the current tenant, sending the job for a queue with the tenant's name, the return the jobId\n\n#### `Enqueue\u003cT\u003e(Expression\u003cFunc\u003cT, Task\u003e\u003e methodCall)`\n\nIt will enqueue a job execution of type `T.Task\u003cMethod\u003e` for the current tenant, sending the job for a queue with the tenant's name, the return the jobId\n\n### Schedule Jobs ⌚\n\nTo Schedule Jobs you just need to use the interface `IMultiTenantBackgroundJobManager`, this interface will have the following methods and extension methods:\n\n#### `ScheduleForAllTenants(Expression\u003cAction\u003e methodCall, TimeSpan delay)`\n\nIt will Schedule a job execution for all the tenant, sending the jobs for a queue with the tenant's name, the return object with consist in a list containing the tenant and the JobId Scheduled for that tenant\n\n#### `ScheduleForAllTenants((Expression\u003cFunc\u003cTask\u003e\u003e methodCall, TimeSpan delay)`\n\nIt will Schedule a job execution for all the tenant, sending the jobs for a queue with the tenant's name, the return object with consist in a list containing the tenant and the JobId Scheduled for that tenant\n\n#### `ScheduleForAllTenants\u003cT\u003e(Expression\u003cAction\u003cT\u003e\u003e methodCall, TimeSpan delay)`\n\nIt will Schedule a job execution of type `T.Method` for all the tenant, sending the jobs for a queue with the tenant's name, the return object with consist in a list containing the tenant and the JobId Scheduled for that tenant\n\n#### `ScheduleForAllTenants\u003cT\u003e(Expression\u003cFunc\u003cT, Task\u003e\u003e methodCall, TimeSpan delay)`\n\nIt will Schedule a job execution of type `T.Task\u003cMethod\u003e` for all the tenant, sending the jobs for a queue with the tenant's name, the return object with consist in a list containing the tenant and the JobId Scheduled for that tenant\n\n#### `Schedule(Expression\u003cAction\u003e methodCall, TimeSpan delay)`\n\nIt will Schedule a job execution for the current tenant, sending the job for a queue with the tenant's name, returning the Scheduled JobId\n\n#### `Schedule(Expression\u003cFunc\u003cTask\u003e\u003e methodCall, TimeSpan delay)`\n\nIt will enqueue a job execution for the current tenant, sending the job for a queue with the tenant's name, returning the Scheduled JobId\n\n#### `Schedule\u003cT\u003e(Expression\u003cAction\u003cT\u003e\u003e methodCall, TimeSpan delay)`\n\nIt will enqueue a job execution of type `T.Method` for the current tenant, sending the job for a queue with the tenant's name, returning the Scheduled JobId\n\n#### `Schedule\u003cT\u003e(Expression\u003cFunc\u003cT, Task\u003e\u003e methodCall, TimeSpan delay)`\n\nIt will enqueue a job execution of type `T.Task\u003cMethod\u003e` for the current tenant, sending the job for a queue with the tenant's name, returning the Scheduled JobId\n\n## Usage / Samples\n\nYou can find some examples of how to use this library in the folder `/samples` with WebApi and Hangfire examples\n\n### Web API\n\nTo enable it on your api you first need to add the services:\n\n```csharp\nbuilder.Services.AddMultiTenant();\n```\n\nthen you need also to register the middleware used to identify the tenant based on the `HttpContext`\n\n```csharp\napp.UseMultiTenant();\n```\n\nOnce that is done you will be able to use the interface `IMultiTenantConfiguration`, this interface will behave the same as the `IConfiguration` interface, but contain only the current tenant settings:\n\nExample:\n\n```csharp\napp.MapGet(\"/\", (IMultiTenantConfiguration configuration) =\u003e\n{\n    return new\n    {\n        Tenant = configuration.Tenant,\n        Message = configuration[\"Message\"],\n    };\n});\n```\n\n### EF Core Migrations:\n\nTo use it with EF Core is quite simple, you need to use the interface `IMultiTenantConfiguration` as mentioned above to setup your EF Context\n\n#### Setup\n\n```csharp\nbuilder.Services.AddDbContext\u003cDataBaseContext\u003e(\n    (IServiceProvider serviceProvider, DbContextOptionsBuilder options) =\u003e\n    {\n        var configuration = serviceProvider.GetRequiredService\u003cIMultiTenantConfiguration\u003e();\n\n        options.UseSqlServer(configuration?[\"ConnectionStrings:DefaultConnection\"]);\n    });\n```\n\n#### Migrations\n\nTo apply the migrations, you only need to do this:\n\n```csharp\nvar tenants = app.Services.GetRequiredService\u003cMultiTenantSettings\u003e();\n\nforeach (var tenant in tenants.Tenants)\n{\n    using (var scope = app.Services.CreateScope())\n    {\n        var tenantSettings = scope.ServiceProvider.GetRequiredService\u003cTenantSettings\u003e();\n\n        tenantSettings.SetCurrentTenant(tenant);\n\n        var db = scope.ServiceProvider.GetRequiredService\u003cDataBaseContext\u003e();\n\n        await db.Database.MigrateAsync();\n    }\n}\n\napp.Run();\n```\n\n## Extras\n\nWe know that settings can be changed all the time, but to get our applications running on with the latest settings we need to restart the application, it caused downtime and it's not very practical. Keeping that in mind, we added also an endpoint that when called will refresh all the settings for all the tenants or a specific tenant:\n\n- `/refresh-settings`\n- `/refresh-settings/{tenantName}`\n\nPS: this will work only with AzureAppConfiguration and SpringCloudConfig\n\n## Roadmap\n\n- [ ] Add unit tests 🧪\n- [x] Add new Config Source\n- [x] Load the Tenants from a web-api request\n- [x] Enable the usage with HangFire\n- [x] Update documentation with new features\n\nSee the [open issues](https://github.com/Pub-Dev/Mellon.MultiTenant/issues) for a full list of proposed features (and known issues).\n\n## Contributing\n\nContributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.\n\nIf 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\".\nDon't forget to give the project a star! Thanks again!\n\n1. Fork the Project\n2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)\n3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)\n4. Push to the Branch (`git push origin feature/AmazingFeature`)\n5. Open a Pull Request\n\n## Contact\n\n- Humberto Rodrigues - [@1bberto](https://instagram.com/1bberto) - humberto_henrique1@live.com\n- Rafael Nagai - [@naganaga](https://instagram.com/rafakenji23) - rafakenji23@gmail.com\n\nProject Page: [https://pub-dev.github.io/Mellon.MultiTenant](https://pub-dev.github.io/Mellon.MultiTenant)\n\n[contributors-shield]: https://img.shields.io/github/contributors/1bberto/Mellon.MultiTenant.svg?style=for-the-badge\n[contributors-url]: https://github.com/Pub-Dev/Mellon.MultiTenant/graphs/contributors\n[forks-shield]: https://img.shields.io/github/forks/1bberto/Mellon.MultiTenant.svg?style=for-the-badge\n[forks-url]: https://github.com/Pub-Dev/Mellon.MultiTenant/network/members\n[stars-shield]: https://img.shields.io/github/stars/1bberto/Mellon.MultiTenant.svg?style=for-the-badge\n[stars-url]: https://github.com/Pub-Dev/Mellon.MultiTenant/stargazers\n[issues-shield]: https://img.shields.io/github/issues/1bberto/Mellon.MultiTenant.svg?style=for-the-badge\n[issues-url]: https://github.com/Pub-Dev/Mellon.MultiTenant/issues\n[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge\u0026logo=linkedin\u0026colorB=555\n[linkedin-url]: https://linkedin.com/in/humbberto\n[linkedin2-url]: https://br.linkedin.com/in/rafakenji\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpub-dev%2Fmellon.multitenant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpub-dev%2Fmellon.multitenant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpub-dev%2Fmellon.multitenant/lists"}