-
Notifications
You must be signed in to change notification settings - Fork 14
Multi Tenancy

Multi-tenancy is an architecture that enables a web service to serve multiple customers or multiple customer sites. (I.e., multiple customers or sites are served from the same URL.) The customers or sites may share some or all data files, but the service must be able to distinguish one customer/site's request from another.
Implementing multi-tenancy in Harmony Core is a straightforward process that involves limited project customization if you used the Harmony Core Solution Templates to create your Harmony Core solution.
Use the StartupCustom.dbl file in the Service project to implement partial methods referenced in the generated Startup.dbl file. If your Services project does not have this file, start by creating it. It will need to include a partial Services.Startup class with partial ConfigureServiceCustom, ConfigureCustomBeforeMvc and ConfigureCustom methods. For example:
import Harmony.AspNetCore.Context
import Harmony.Core.Interface
import Harmony.Core
import Harmony.Core.FileIO
import Microsoft.AspNetCore.Builder
import Microsoft.AspNetCore.Hosting
import Microsoft.AspNetCore.Http
import Microsoft.Extensions.Primitives
import Microsoft.Extensions.DependencyInjection
import Microsoft.Extensions.DependencyInjection.Extensions
import System.Linq
namespace Services
public partial class Startup
partial method ConfigureServicesCustom, void
services, @IServiceCollection
proc
endmethod
partial method ConfigureCustomBeforeMvc, void
required in app, @IApplicationBuilder
required in env, @IHostingEnvironment
proc
app.UseMultiTenancy(GetTenantId)
endmethod
partial method ConfigureCustom, void
required in app, @IApplicationBuilder
required in env, @IHostingEnvironment
proc
endmethod
endclass
endnamespace
The ConfigureCustomBeforeMvc method must include a call to the UseMultiTenancy extension method (in the Harmony.AspNetCore.Context namespace), which adds Harmony Core multi-tenancy middleware to the ASP.NET Core processing pipeline:
app.UseMultiTenancy(GetTenantId)
In this call, GetTenantId is the name of a method you implement that determines which customer/site this request is intended for. The following is a simple example that determines this based on a custom HTTP header: x-tenant-id:
private method GetTenantId, @string
httpCtx, @HttpContext
proc
data headers, StringValues
if(httpCtx.Request.Headers.TryGetValue("X-TENANT-ID", headers) && headers.Count == 1) then
begin
mreturn headers.ToArray().First()
end
else
mreturn String.Empty
endmethod
This method could instead use a property of the request URL, a cookie, or something stored in the user's authentication information. Whatever the source, if this information is returned from GetTenantId, it will be stored and made available to all running code associated with the async context. I.e., it will be available anywhere in the processing pipeline for the request.
Now that the tenant ID is available to the rest of our code, we need to make sure the EF provider uses the right data file. This is done by replacing the default Harmony Core implementation of Harmony.Core.FileIO.IFileChannelManager. The Harmony.Core.FileIO namespace includes two base classes for this:
-
If you need to support IOHooks, inherit from
HookableFileChannelManager. -
Otherwise, inherit from
FileChannelManager.
The following is a FileChannelManager example, but the methods are the same in either case:
import Harmony.Core.FileIO
import Harmony.AspNetCore
namespace Services
public class CustomFileSpecResolver extends FileChannelManager
public override method GetChannel, int
required in fileName, @string
required in openMode, FileOpenMode
proc
data newFileName = fileName
if(fileName == "DAT:custmas.ism")
newFileName = MultiTenantProvider.TenantId + ":custmas.ism"
mreturn parent.GetChannel(newFileName,openMode)
endmethod
endclass
endnamespace
The code in this snippet intercepts the filename before it is passed to OPEN. If the file is the one the code is looking for (custmas.ism in this case), its logical is reset to include the tenant ID that was set earlier in the request pipeline. You will almost certainly need to change this implementation to handle your specific environment.
Now that we have a custom implementation for IFileChannelManager, we need to register it for dependency injection. This is done in the ConfigureServicesCustom method we created earlier in StartupCustom.dbl. All that needs to be added is the following line:
services.AddSingleton<IFileChannelManager, CustomFileSpecResolver>()
This replaces the default FileChannelManager with CustomFileSpecResolver, enabling the web service to support multiple sites or customers. If there are security concerns, you should either prevent users from directly specifying their tenant ID, or validate it using authentication/authorization data contained in the HttpContext.
-
Tutorial 2: Building a Service from Scratch
- Creating a Basic Solution
- Enabling OData Support
- Configuring Self Hosting
- Entity Collection Endpoints
- API Documentation
- Single Entity Endpoints
- OData Query Support
- Alternate Key Endpoints
- Expanding Relations
- Postman Tests
- Supporting CRUD Operations
- Adding a Primary Key Factory
- Adding Create Endpoints
- Adding Upsert Endpoints
- Adding Patch Endpoints
- Adding Delete Endpoints
-
Harmony Core CLI Tool
-
OData Aware Tools
-
Advanced Topics
- CLI Tool Customization
- Adapters
- API Versioning
- Authentication
- Authorization
- Collection Counts
- Customization File
- Custom Field Types
- Custom File Specs
- Custom Properties
- Customizing Generated Code
- Deploying to Linux
- Dynamic Call Protocol
- Environment Variables
- Field Security
- File I/O
- Improving AppSettings Processing
- Logging
- Optimistic Concurrency
- Multi-Tenancy
- Publishing in IIS
- Repeatable Unit Tests
- Stored Procedure Routing
- Suppressing OData Metadata
- Traditional Bridge
- Unit Testing
- EF Core Optimization
- Updating a Harmony Core Solution
- Updating to 3.1.90
- Creating a new Release
-
Background Information