{"id":120169,"date":"2024-06-25T08:08:36","date_gmt":"2024-06-25T06:08:36","guid":{"rendered":"https:\/\/code-maze.com\/?p=120169"},"modified":"2024-06-25T08:09:58","modified_gmt":"2024-06-25T06:09:58","slug":"dotnet-multitenancy-with-orchard-core","status":"publish","type":"post","link":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/","title":{"rendered":"Multitenancy in .NET With Orchard Core"},"content":{"rendered":"<p>Orchard Core, a multipurpose open-source platform built upon ASP.NET Core, offers strong support for multi-tenancy. This article explores the installation and configuration of Orchard Core for multitenancy .NET applications, as well as the setup of multitenancy through its admin dashboard and framework.<\/p>\n<p>Additionally, we&#8217;ll look into the integration of features and modules to extend our application&#8217;s functionalities and dynamically create new tenants using the Orchard Core framework.<\/p>\n<div style=\"padding: 20px; border-left: 5px #dc2323 solid; display: block; margin-bottom: 20px; box-shadow: 1px 1px 5px 0px lightgrey;\">To download the source code for this article, you can visit our <a href=\"https:\/\/github.com\/CodeMazeBlog\/CodeMazeGuides\/tree\/main\/dotnet-projects\/MultiTenantOrchardCore\" target=\"_blank\" rel=\"nofollow noopener\">GitHub repository<\/a>.<\/div>\n<h2>What is a Multitenancy Application?<\/h2>\n<p>Multitenant applications are software that efficiently shares resources and databases among multiple tenants, which allows an instance of the software and its infrastructure to serve multiple customers simultaneously. For more on multitenant applications, see our article on <a href=\"https:\/\/code-maze.com\/aspnetcore-multitenant-application\/\" target=\"_blank\" rel=\"noopener\">How to Build a Multitenant Application with ASP.NET Core<\/a><\/p>\n<h2>What is Orchard Core?<\/h2>\n<p>As described on the <a href=\"https:\/\/github.com\/OrchardCMS\/OrchardCore\" target=\"_blank\" rel=\"nofollow noopener\">Orchard Core GitHub page<\/a>, Orchard Core is designed to be modular and support multi-tenancy, making it adaptable to various web application needs.<\/p>\n<p>There are two primary targets in Orchard Core.<\/p>\n<h3>Orchard Core Framework<\/h3>\n<p>This aspect of Orchard Core functions as an application framework, which provides us with a way to build modular and multi-tenant applications on ASP.NET Core. Hence, it offers a flexible architecture that enables us to create solutions to fit specific project requirements.\u00a0<\/p>\n<h3>Orchard Core CMS<\/h3>\n<p>Orchard Core CMS provides out-of-the-box content management capabilities. It is a Web Content Management System (CMS) that sits on the Orchard Core Framework as its base. It offers powerful content management features, allowing us to create, edit, and publish content easily.<\/p>\n<h2>Installing and Configuring Orchard Core Application<\/h2>\n<p>We can install Orchard Core through two primary methods: manual installation via NuGet packages or code generation using <code>dotnet new<\/code> templates. While both approaches have their merits, we&#8217;ll focus on the manual method in this guide to gain a deeper understanding of the installation process.<\/p>\n<p>Alright, let&#8217;s begin by launching a command prompt, and create a new ASP.NET Core Web App (Razor Pages) project, and name it <code>MyOrchardCoreCMS<\/code> with this command:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">dotnet new webapp -o MyOrchardCoreCMS<\/code><\/p>\n<p>Also, let&#8217;s add the Orchard Core CMS NuGet package to the project:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">dotnet add package OrchardCore.Application.Cms.Targets<\/code><\/p>\n<p>Alternatively, if we prefer a minimal setup with only <code>TheAdmin<\/code> theme included, we can use <code>OrchardCore.Application.Cms.Core.Targets<\/code> instead. For this demo, we&#8217;ll go with the former.<\/p>\n<p>Let&#8217;s make the necessary adjustments to the <code>Program.cs<\/code> class to integrate Orchard Core into our application:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">var builder = WebApplication.CreateBuilder(args);\r\n\r\nbuilder.Services.AddOrchardCms();\r\n\r\nvar app = builder.Build();\r\n\r\nif (!app.Environment.IsDevelopment())\r\n{\r\n    app.UseExceptionHandler(\"\/Error\");\r\n    app.UseHsts();\r\n}\r\n\r\napp.UseStaticFiles();\r\napp.UseOrchardCore();\r\n\r\napp.Run();<\/pre>\n<p>First, the <code>AddOrchardCms()<\/code> method adds all the necessary services, modules, and configurations to enable Orchard Core CMS into our application. This includes services for content management, authentication, authorization, media library, themes, and more.<\/p>\n<p>Likewise, <code>UseOrchardCore()<\/code> method sets up the middleware and modules that Orchard Core needs to handle incoming HTTP requests like routing, authentication, authorization, and other functionalities managed by Orchard Core CMS.<\/p>\n<h3>Launching and Setting Up Orchard Core CMS<\/h3>\n<p>Let us build and run the project to launch our Orchard Core CMS website. Upon launching, the setup screen will display for us to fill in the necessary information:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCoreSiteSetup.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-120170 size-full\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCoreSiteSetup.png\" alt=\"Orchard Core CMS tenant setup\" width=\"900\" height=\"529\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCoreSiteSetup.png 900w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCoreSiteSetup-300x176.png 300w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCoreSiteSetup-768x451.png 768w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/a><\/p>\n<p>After filling in the required information and clicking the &#8216;Finish Setup&#8217; button to finalize the configuration, our website will be created and ready for us to start multitenant websites. Let&#8217;s see that.<\/p>\n<h2>Configuring Multitenancy With Orchard Core Admin Dashboard<\/h2>\n<p>Orchard Core makes it easy to create multitenant websites through its admin dashboard. Let&#8217;s see how we can get into configuring multitenancy:<\/p>\n<p>We&#8217;ll begin by navigating to <code>\/admin<\/code> of the CMS website base address. This will redirect us to the login page. We&#8217;ll land on the admin dashboard after successful authentication with our login details.<\/p>\n<p>To add a new tenant, let&#8217;s expand the &#8216;Multi-Tenancy&#8217; menu and select &#8216;Tenants&#8217;. Currently, we have only the default tenant.<\/p>\n<p>Let&#8217;s go ahead and add another tenant by clicking the &#8216;Add Tenant&#8217; button to start the creation process:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantsPage.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-120171 size-full\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantsPage.png\" alt=\"Tenants page\" width=\"900\" height=\"195\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantsPage.png 900w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantsPage-300x65.png 300w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantsPage-768x166.png 768w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/a><\/p>\n<p>Let&#8217;s fill in some of the fields to define the new tenant and click the &#8216;Create Tenant&#8217; button to complete this stage of the tenant creation process using the Create Tenant page:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-CreateTenant.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-120172 size-full\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-CreateTenant.png\" alt=\"Create Tenant page in the Orchard Core\" width=\"900\" height=\"563\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-CreateTenant.png 900w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-CreateTenant-300x188.png 300w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-CreateTenant-768x480.png 768w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-CreateTenant-400x250.png 400w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/a><\/p>\n<p>However, note that the new tenant isn&#8217;t yet publicly accessible. We need to complete the setup before Orchard Core starts running the tenant.<\/p>\n<p>Finally, let&#8217;s finish the setup by clicking the &#8216;Setup&#8217; button for our new Blog tenant. Here, let&#8217;s fill in the required information and submit the form:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantsListPage.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-120173 size-full\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantsListPage.png\" alt=\"Tenant list with setup button\" width=\"900\" height=\"283\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantsListPage.png 900w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantsListPage-300x94.png 300w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantsListPage-768x241.png 768w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Orchard Core tenant setup page:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantSetup.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-120174 size-full\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantSetup.png\" alt=\"Orchard Core tenant setup\" width=\"900\" height=\"440\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantSetup.png 900w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantSetup-300x147.png 300w, https:\/\/code-maze.com\/wp-content\/uploads\/2024\/06\/OrchardCore-TenantSetup-768x375.png 768w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/a><\/p>\n<p>Upon completion, Orchard Core will redirect us to the new tenant&#8217;s homepage.<\/p>\n<h2>Configuring Multitenancy with Orchard Core Framework<\/h2>\n<p>As we have seen, the Orchard Core CMS framework offers a solid solution for developing applications that serve multiple tenants.<\/p>\n<p>However, what if we want to implement multitenancy in an ASP.NET Core application without the overhead of integrating the entire Orchard Core CMS framework? Fortunately, the Orchard Core framework provides a multitenancy feature that can be seamlessly integrated into ASP.NET Core applications without using the CMS.<\/p>\n<h3>Setting up Multitenancy<\/h3>\n<p>To demonstrate how to configure multitenancy with Orchard Core in an ASP.NET Core application, let&#8217;s create a new project ASP.NET Core Web App with Razor Pages with the name <code>MultiTenantApp<\/code>.<\/p>\n<p>Additionally, we need to add the Orchard Core MVC NuGet package to our project:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">dotnet add package OrchardCore.Application.Mvc.Targets<\/code>\u00a0<\/p>\n<p>This package provides the necessary functionality for integrating Orchard Core into our ASP.NET Core application.<\/p>\n<p>Again, let&#8217;s modify the <code>Program.cs<\/code> class with the necessary codes to integrate Orchard Core multitenancy functionalities into our application:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">var builder = WebApplication.CreateBuilder(args);\r\n\r\nbuilder.Services.AddOrchardCore().AddMvc().WithTenants();\r\n\r\nvar app = builder.Build();\r\n\r\nif (!app.Environment.IsDevelopment())\r\n{\r\n    app.UseExceptionHandler(\"\/Error\");\r\n    app.UseHsts();\r\n}\r\n\r\napp.UseHttpsRedirection();\r\napp.UseStaticFiles();\r\napp.UseOrchardCore();\r\n\r\napp.Run();<\/pre>\n<p>We call the <code>AddOrchardCore()<\/code> method to register the core Orchard Core services required by Orchard Core to our application&#8217;s dependency injection container.<\/p>\n<p>Furthermore, we also use the <code>WithTenants()<\/code> method to configure our application to support multiple tenancy functionalities. Finally, the <code>UseOrchardCore()<\/code> method adds Orchard Core middleware to our application&#8217;s middleware pipeline for handling requests.<\/p>\n<h3>Configuring Tenants and Tenant-Specific Settings<\/h3>\n<p>Now that our application is set up to use Orchard Core&#8217;s multitenancy feature, let&#8217;s configure it in the <code>appSettings.json<\/code> file:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"json\">\"OrchardCore\": {\r\n    \"Default\": {\r\n      \"State\": \"Running\",\r\n      \"RequestUrlHost\": null,\r\n      \"RequestUrlPrefix\": null,\r\n      \"CustomTitle\": \"Default Tenant\",\r\n      \"CustomSetting\": \"Custom setting for Default tenant\"\r\n    },\r\n    \"CustomerA\": {\r\n      \"State\": \"Running\",\r\n      \"RequestUrlHost\": null,\r\n      \"RequestUrlPrefix\": \"customer-a\",\r\n      \"CustomTitle\": \"Customer A\",\r\n      \"CustomSetting\": \"Custom setting for Customer A\"\r\n    },\r\n    \"CustomerB\": {\r\n      \"State\": \"Running\",\r\n      \"RequestUrlHost\": null,\r\n      \"RequestUrlPrefix\": \"customer-b\",\r\n      \"CustomTitle\": \"Customer B\",\r\n      \"CustomSetting\": \"Custom setting for Customer B\"\r\n    }\r\n}\r\n<\/pre>\n<p>Orchard Core extends ASP.NET Core <code>IConfiguration<\/code> through <code>IShellConfiguration<\/code>, enabling configuration specific to individual tenants in the global application configuration. Essentially, Orchard Core automatically accesses the OrchardCore section in <code>appSettings.json<\/code>, eliminating the need for us to write custom code for this purpose.<\/p>\n<p>Here is what is happening in this configuration:<\/p>\n<ul>\n<li><strong>OrchardCore:<\/strong> This top-level section contains all Orchard Core-related settings.<\/li>\n<li><strong>Default, CustomerA,<\/strong> and <strong>CustomerB<\/strong>: These represent different tenants of the application, each with its configuration settings.<\/li>\n<li><strong>State<\/strong>: Indicates the state of each tenant, with all tenants set to &#8220;Running&#8221; in this example.<\/li>\n<li><strong>RequestUrlHost<\/strong>: Specifies the host part of the URL for each tenant. It&#8217;s set to null, meaning it will inherit from the parent host.<\/li>\n<li><strong>RequestUrlPrefix<\/strong>: Specifies a prefix to use in the URL for each tenant. For example, &#8220;customer-a&#8221; and &#8220;customer-b&#8221; prefixes are defined for CustomerA and CustomerB tenants respectively.<\/li>\n<li><strong>CustomTitle<\/strong>: Custom title for each tenant, which can be displayed in the application UI or used for identification purposes.<\/li>\n<li><strong>CustomSetting<\/strong>: Additional custom settings specific to each tenant, allowing for fine-grained configuration<\/li>\n<\/ul>\n<h2>Understanding Features and Modules of Orchard Core<\/h2>\n<p>In Orchard Core, features and modules serve as foundational concepts that enable modular and scalable application development. Features represent units of functionality within Orchard Core, bundling together related components or functionalities that can be enabled or disabled independently.<\/p>\n<p>Specifically, each feature typically contributes a unique aspect or capability to the application, which contains various components that furnish different functionalities to the system. Orchard Core simplifies feature management by providing administrative dashboard controls within the Orchard Core CMS as we&#8217;ve seen, alongside configuration options in <code>appSettings.json<\/code> or programmatically through the Orchard Core API.<\/p>\n<p>On the other hand, Modules act as the base elements of an Orchard Core application. These are <code>Class Library<\/code> applications containing code, templates, assets, and other necessary resources to expand the application&#8217;s capabilities.<\/p>\n<p>For example, modules can define one or more features and provide implementations for those features, thereby encapsulating specific functionalities or reusable components that integrate with an Orchard Core application.<\/p>\n<h3>Integrating Features and Modules in Tenants<\/h3>\n<p>Currently, our application has three tenants, <code>Default<\/code>, <code>CustomerA<\/code> and <code>CustomerB.<\/code> Let&#8217;s assume we want to extend our application with the capability of sending email and SMS. That&#8217;s where Orchard Core modules come in.<\/p>\n<p>However, these features are not going to be available for all customers. <code>CustomerA<\/code> will have only the email feature enabled, while <code>CustomerB<\/code> will have both the email and SMS features available.<\/p>\n<p>To begin, let&#8217;s create two separate Orchard Core modules: one for handling email functionality and another for managing SMS capabilities.<\/p>\n<p>Let&#8217;s create a new <code>Class Library<\/code> project, and name it <code>EmailModule<\/code>. Next, let&#8217;s add the OrchardCore Module NuGet package to the <code>EmailModule<\/code> project:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">dotnet add package OrchardCore.Module.Targets<\/code><\/p>\n<p>The <code>OrchardCore.Module.Targets<\/code> NuGet package serves as a way to indicate to Orchard Core that this library functions as an Orchard Core module.<\/p>\n<p>Next, we&#8217;ll create a class and name it <code>Startup.cs<\/code> in the <code>EmailModule<\/code> project. Orchard Core will read this class during the initialization of the module:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public void Configure(IEndpointRouteBuilder endpoints)\r\n{\r\n    endpoints.MapGet(\"\/Email\", async context =&gt; { await context.Response.WriteAsync(\"Welcome to Email module\"); });\r\n}<\/pre>\n<p>We set up an endpoint route to handle HTTP GET requests to the &#8220;\/Email&#8221; path of the email module.\u00a0<\/p>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\">Similarly, we&#8217;ll repeat our process to create the EmailModule for the SMS module. Let&#8217;s create a new <code>Class Library<\/code> project and name it <code>SmsModule<\/code> and then, implement the <code>Startup.cs<\/code> class:<\/div>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public void Configure(IEndpointRouteBuilder endpoints)\r\n{\r\n    endpoints.MapGet(\"\/Sms\", async context =&gt; { await context.Response.WriteAsync(\"Welcome to SMS module\"); });\r\n}<\/pre>\n<\/div>\n<div>\n<p>Next, let&#8217;s open and update the <code>appSettings.json<\/code> in the <code>MultiTenantApp<\/code> project:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"json\" data-enlighter-highlight=\"5,13\">\"CustomerA\": {\r\n  \"State\": \"Running\",\r\n  \"RequestUrlHost\": null,\r\n  \"RequestUrlPrefix\": \"customer-a\",\r\n  \"Features\": [ \"EmailModule\" ],\r\n  \"CustomTitle\": \"Customer A\",\r\n  \"CustomSetting\": \"Custom setting for Customer A\"\r\n},\r\n\"CustomerB\": {\r\n  \"State\": \"Running\",\r\n  \"RequestUrlHost\": null,\r\n  \"RequestUrlPrefix\": \"customer-b\",\r\n  \"Features\": [ \"SmsModule\", \"EmailModule\" ],\r\n  \"CustomTitle\": \"Customer B\",\r\n  \"CustomSetting\": \"Custom setting for Customer B\"\r\n}<\/pre>\n<p>The <code>Features<\/code> section lists the specific modules enabled for each tenant. For instance, <code>CustomerA<\/code> has the <code>EmailModule<\/code> enabled exclusively, whereas <code>CustomerB<\/code> enjoys both <code>SmsModule<\/code> and <code>EmailModule<\/code> functionalities enabled.<\/p>\n<p>To access the email module for <code>CustomerA<\/code> we would send a request to <code><span style=\"font-weight: 400;\">localhost:7290<\/span>\/customer-a\/Email<\/code>, where the server responds with the message &#8220;Welcome to Email module&#8221;.<\/p>\n<p>Alternatively, to access the Email module for <code>CustomerB,<\/code> we would replace <code>customer-a<\/code> in the URL with <code>customer-b<\/code> URL prefix.<\/p>\n<p>However, if we try to access the SMS module from CustomerA tenant, the request will fail with an HTTP 404 error message.<\/p>\n<\/div>\n<\/div>\n<div>\n<h2>Creating Tenant Dynamically<\/h2>\n<\/div>\n<div>\n<p>Orchard Core Framework supports dynamically adding tenants, which makes it easy to create and configure new tenants without manual intervention.<\/p>\n<p>To begin, let&#8217;s create a new <code>Class Library<\/code> project and name it <code>DynamicTenantModule<\/code>. Within this project, we&#8217;ll add <code>OrchardCore.Module.Targets<\/code>:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">dotnet add package OrchardCore.Module.Targets<\/code><\/p>\n<p>Also, we need to add <code>OrchardCore.Tenants<\/code>:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">dotnet add package OrchardCore.Tenants<\/code><\/p>\n<p><code>OrchardCore.Tenants<\/code> supports dynamic additions of tenants, allowing for on-the-fly creation and configuration of new tenants without requiring manual intervention.<\/p>\n<p>Additionally, within the <code>DynamicTenantModule<\/code> project, let&#8217;s create a class with the name <code>DynamicTenantSetup.<\/code> This class will be responsible for the actual creation of new tenants:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public class DynamicTenantSetup(IShellHost shellHost, IShellSettingsManager shellSettingsManager)\r\n{\r\n    public async Task CreateTenant(string tenantName, string urlPrefix)\r\n    {\r\n        var shellSettings = new ShellSettings\r\n        {\r\n            Name = tenantName,\r\n            RequestUrlHost = null,\r\n            RequestUrlPrefix = urlPrefix,\r\n            State = TenantState.Uninitialized,\r\n        };\r\n        \r\n        shellSettings[\"customProperty\"] = $\"Custom settings for '{tenantName}'\";\r\n        \r\n        await shellSettingsManager.SaveSettingsAsync(shellSettings);\r\n\r\n        shellSettings.State = TenantState.Running;\r\n\r\n        await shellHost.UpdateShellSettingsAsync(shellSettings);\r\n    }\r\n}<\/pre>\n<p>In the <code>CreateTenant()<\/code> method, we initialize a <code>ShellSettings<\/code> instance to configure settings for the new tenant. Key properties such as <code>Name<\/code>, <code>RequestUrlPrefix,<\/code> and custom properties are set from the <code>tenantName<\/code> and <code>urlPrefix<\/code> parameters, accordingly.<\/p>\n<\/div>\n<p>Finally, we call <code>shellHost.UpdateShellSettingsAsync()<\/code> to update the shell settings with the newly configured settings for the tenant.<\/p>\n<p><code>ShellSettings<\/code> is a class that holds the configuration settings for a tenant. In Orchard Core, each tenant is represented by a <code>ShellSettings<\/code> instance.<\/p>\n<p><code>IShellHost<\/code> is responsible for managing tenants. It acts as a gateway to a specific tenant&#8217;s resources. Also, it provides ways to interact with an individual tenant&#8217;s services and manage its lifecycle.<\/p>\n<p>Next, let&#8217;s add the <code>Startup.cs<\/code> class for this module:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public class Startup\r\n{\r\n    public void ConfigureServices(IServiceCollection services)\r\n    {\r\n        services.AddScoped&lt;DynamicTenantSetup&gt;();\r\n    }\r\n}<\/pre>\n<p>Additionally, let&#8217;s create a TenantsController class to handle HTTP requests for creating new tenants dynamically:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">[ApiController, Route(\"api\/tenants\")]\r\npublic class TenantsController(DynamicTenantSetup tenantSetup) : Controller\r\n{\r\n    public IActionResult Index()\r\n    {\r\n        return Ok(\"Tenant controller home\");\r\n    }\r\n    \r\n    [HttpPost(\"create\")]\r\n    [IgnoreAntiforgeryToken]\r\n    public async Task&lt;IActionResult&gt; Create(string tenantName, string urlPrefix)\r\n    {\r\n        await tenantSetup.CreateTenant(tenantName, urlPrefix);\r\n        \r\n        return Ok($\"Tenant '{tenantName}' created\");\r\n    }\r\n}<\/pre>\n<p>To make the <code>DynamicTenantModule<\/code> available to the <code>Default<\/code> tenant, we&#8217;ll add it to the <code>Default<\/code> tenant&#8217;s section by modifying the <code>appSettings.json<\/code> in the <code>MultiTenantApp<\/code> project:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"json\">\"Default\": {\r\n    \"State\": \"Running\",\r\n    \"RequestUrlHost\": null,\r\n    \"RequestUrlPrefix\": null,\r\n    \"Features\": [\"DynamicTenantModule\"],\r\n    \"CustomTitle\": \"Default Tenant\",\r\n    \"CustomSetting\": \"Custom setting for Default tenant\"\r\n}\r\n<\/pre>\n<p>Finally, to ensure the module is available to our application, we reference the <code>DynamicTenantModule<\/code> in the <code>MultiTenantApp<\/code> project.<\/p>\n<p>Ultimately, our application is now able to create new tenants dynamically. For example, to dynamically create a new tenant, we&#8217;ll send a <code>POST<\/code> request to <code>https:\/\/<span style=\"font-weight: 400;\">localhost:7290<\/span>\/api\/tenants\/create<\/code>, providing the <code>tenantName<\/code> and <code>urlPrefix<\/code> arguments, respectively.<\/p>\n<h2>Conclusion<\/h2>\n<p>Multitenancy applications provide efficient solutions for sharing resources among multiple tenants, particularly in Software-as-a-Service (SaaS) delivery. Conversely, Orchard Core is a versatile platform that supports application framework and content management system (CMS) functionalities, built upon ASP.NET Core.<\/p>\n<p>In other words, through its modular architecture and support for multitenancy, Orchard Core enables us to develop scalable and customizable multi-tenant applications.<\/p>\n<p>Moreover, Orchard Core simplifies the integration of features and modules, enabling us to easily extend our application capabilities dynamically. By leveraging features and modules, we can enhance application functionalities using the inbuilt modular application capabilities Orchard Core provides us.<\/p>\n<p>Additionally, the Orchard Core framework offers support for dynamically creating tenants, which eliminates the need for manual intervention in setting up new tenants. Through the DynamicTenantModule, we saw how we can automate the process of tenant creation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Orchard Core, a multipurpose open-source platform built upon ASP.NET Core, offers strong support for multi-tenancy. This article explores the installation and configuration of Orchard Core for multitenancy .NET applications, as well as the setup of multitenancy through its admin dashboard and framework. Additionally, we&#8217;ll look into the integration of features and modules to extend our [&hellip;]<\/p>\n","protected":false},"author":140,"featured_media":62191,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"categories":[2159,2081],"tags":[79,1811,1529,1398,2224],"class_list":["post-120169","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-asp-net-core","category-client-library","tag-asp-net-core","tag-c","tag-client-library","tag-multitenancy","tag-orchard-core","et-has-post-format-content","et_post_format-et-post-format-standard"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Multitenancy in .NET With Orchard Core - Code Maze<\/title>\n<meta name=\"description\" content=\"This article explores the installation and configuration of Orchard Core for multitenancy .NET applications.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Multitenancy in .NET With Orchard Core - Code Maze\" \/>\n<meta property=\"og:description\" content=\"This article explores the installation and configuration of Orchard Core for multitenancy .NET applications.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/\" \/>\n<meta property=\"og:site_name\" content=\"Code Maze\" \/>\n<meta property=\"article:published_time\" content=\"2024-06-25T06:08:36+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-06-25T06:09:58+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-dotnet-core.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1100\" \/>\n\t<meta property=\"og:image:height\" content=\"620\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Emeka Emego\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/EmekaEmego\" \/>\n<meta name=\"twitter:site\" content=\"@CodeMazeBlog\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Emeka Emego\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":[\"Article\",\"BlogPosting\"],\"@id\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/\"},\"author\":{\"name\":\"Emeka Emego\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/person\/2b7c18af5950a2341a54b5da8d0c79b9\"},\"headline\":\"Multitenancy in .NET With Orchard Core\",\"datePublished\":\"2024-06-25T06:08:36+00:00\",\"dateModified\":\"2024-06-25T06:09:58+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/\"},\"wordCount\":2011,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/code-maze.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-dotnet-core.png\",\"keywords\":[\"asp.net core\",\"C#\",\"Client Library\",\"Multitenancy\",\"Orchard Core\"],\"articleSection\":[\"ASP.NET Core\",\"Client Library\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/\",\"url\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/\",\"name\":\"Multitenancy in .NET With Orchard Core - Code Maze\",\"isPartOf\":{\"@id\":\"https:\/\/code-maze.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-dotnet-core.png\",\"datePublished\":\"2024-06-25T06:08:36+00:00\",\"dateModified\":\"2024-06-25T06:09:58+00:00\",\"description\":\"This article explores the installation and configuration of Orchard Core for multitenancy .NET applications.\",\"breadcrumb\":{\"@id\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#primaryimage\",\"url\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-dotnet-core.png\",\"contentUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-dotnet-core.png\",\"width\":1100,\"height\":620,\"caption\":\".NET (Core)\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/code-maze.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Multitenancy in .NET With Orchard Core\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/code-maze.com\/#website\",\"url\":\"https:\/\/code-maze.com\/\",\"name\":\"Code Maze\",\"description\":\"Learn. Code. Succeed.\",\"publisher\":{\"@id\":\"https:\/\/code-maze.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/code-maze.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/code-maze.com\/#organization\",\"name\":\"Code Maze\",\"url\":\"https:\/\/code-maze.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png\",\"contentUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png\",\"width\":3511,\"height\":3510,\"caption\":\"Code Maze\"},\"image\":{\"@id\":\"https:\/\/code-maze.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/CodeMazeBlog\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/person\/2b7c18af5950a2341a54b5da8d0c79b9\",\"name\":\"Emeka Emego\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/03\/emeka-emego-400px-150x150.png\",\"contentUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/03\/emeka-emego-400px-150x150.png\",\"caption\":\"Emeka Emego\"},\"description\":\"Emeka is an experienced software developer and educator with over 13 years of hands-on experience in the industry. He possesses a wide range of skills in web application development, covering both front-end and back-end technologies. Over the years, Emeka has collaborated with diverse teams, interacting with key decision-makers and stakeholders to understand business goals and needs. He shares his technical expertise through thorough training sessions while fostering the growth of future developers.\",\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/emekaemego\/\",\"https:\/\/x.com\/https:\/\/twitter.com\/EmekaEmego\"],\"url\":\"https:\/\/code-maze.com\/author\/emego-emeka\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Multitenancy in .NET With Orchard Core - Code Maze","description":"This article explores the installation and configuration of Orchard Core for multitenancy .NET applications.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/","og_locale":"en_US","og_type":"article","og_title":"Multitenancy in .NET With Orchard Core - Code Maze","og_description":"This article explores the installation and configuration of Orchard Core for multitenancy .NET applications.","og_url":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/","og_site_name":"Code Maze","article_published_time":"2024-06-25T06:08:36+00:00","article_modified_time":"2024-06-25T06:09:58+00:00","og_image":[{"width":1100,"height":620,"url":"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-dotnet-core.png","type":"image\/png"}],"author":"Emeka Emego","twitter_card":"summary_large_image","twitter_creator":"@https:\/\/twitter.com\/EmekaEmego","twitter_site":"@CodeMazeBlog","twitter_misc":{"Written by":"Emeka Emego","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":["Article","BlogPosting"],"@id":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#article","isPartOf":{"@id":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/"},"author":{"name":"Emeka Emego","@id":"https:\/\/code-maze.com\/#\/schema\/person\/2b7c18af5950a2341a54b5da8d0c79b9"},"headline":"Multitenancy in .NET With Orchard Core","datePublished":"2024-06-25T06:08:36+00:00","dateModified":"2024-06-25T06:09:58+00:00","mainEntityOfPage":{"@id":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/"},"wordCount":2011,"commentCount":0,"publisher":{"@id":"https:\/\/code-maze.com\/#organization"},"image":{"@id":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#primaryimage"},"thumbnailUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-dotnet-core.png","keywords":["asp.net core","C#","Client Library","Multitenancy","Orchard Core"],"articleSection":["ASP.NET Core","Client Library"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/","url":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/","name":"Multitenancy in .NET With Orchard Core - Code Maze","isPartOf":{"@id":"https:\/\/code-maze.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#primaryimage"},"image":{"@id":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#primaryimage"},"thumbnailUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-dotnet-core.png","datePublished":"2024-06-25T06:08:36+00:00","dateModified":"2024-06-25T06:09:58+00:00","description":"This article explores the installation and configuration of Orchard Core for multitenancy .NET applications.","breadcrumb":{"@id":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#primaryimage","url":"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-dotnet-core.png","contentUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2021\/12\/social-dotnet-core.png","width":1100,"height":620,"caption":".NET (Core)"},{"@type":"BreadcrumbList","@id":"https:\/\/code-maze.com\/dotnet-multitenancy-with-orchard-core\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/code-maze.com\/"},{"@type":"ListItem","position":2,"name":"Multitenancy in .NET With Orchard Core"}]},{"@type":"WebSite","@id":"https:\/\/code-maze.com\/#website","url":"https:\/\/code-maze.com\/","name":"Code Maze","description":"Learn. Code. Succeed.","publisher":{"@id":"https:\/\/code-maze.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/code-maze.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/code-maze.com\/#organization","name":"Code Maze","url":"https:\/\/code-maze.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/code-maze.com\/#\/schema\/logo\/image\/","url":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png","contentUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png","width":3511,"height":3510,"caption":"Code Maze"},"image":{"@id":"https:\/\/code-maze.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/CodeMazeBlog"]},{"@type":"Person","@id":"https:\/\/code-maze.com\/#\/schema\/person\/2b7c18af5950a2341a54b5da8d0c79b9","name":"Emeka Emego","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/code-maze.com\/#\/schema\/person\/image\/","url":"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/03\/emeka-emego-400px-150x150.png","contentUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2024\/03\/emeka-emego-400px-150x150.png","caption":"Emeka Emego"},"description":"Emeka is an experienced software developer and educator with over 13 years of hands-on experience in the industry. He possesses a wide range of skills in web application development, covering both front-end and back-end technologies. Over the years, Emeka has collaborated with diverse teams, interacting with key decision-makers and stakeholders to understand business goals and needs. He shares his technical expertise through thorough training sessions while fostering the growth of future developers.","sameAs":["https:\/\/www.linkedin.com\/in\/emekaemego\/","https:\/\/x.com\/https:\/\/twitter.com\/EmekaEmego"],"url":"https:\/\/code-maze.com\/author\/emego-emeka\/"}]}},"_links":{"self":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts\/120169","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/users\/140"}],"replies":[{"embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/comments?post=120169"}],"version-history":[{"count":2,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts\/120169\/revisions"}],"predecessor-version":[{"id":120177,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts\/120169\/revisions\/120177"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/media\/62191"}],"wp:attachment":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/media?parent=120169"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/categories?post=120169"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/tags?post=120169"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}