-
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 Code Generator
-
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