Introducing TinySaas: a library for building multitenant applications in ASP.NET Core
Some years back, I used to maintain a SaaS application built on ASP.NET WebForms. Many organizations are aiming to migrate existing applications built on the ASP.NET Framework to the amazing .NET Core.
TinySaas culminated from my work in migrating an ASP.NET legacy application to ASP.NET Core.
Why TinySaas?
There abound on the internet several frameworks that aim to allow developers to build multitenant applications using the .NET Core Framework, but the keyword (Framework).
TinySaas is a library that aims to add multitenancy support to new and existing ASP.NET Core applications without forcing the adoption of a new framework.
The credit for this library goes to Gunnar and Michael, two great authors whose several blog articles on this topic served as the foundation for this simple library.
Getting Started
Multitenancy support should not require a rewrite nor massive changes. With TinyTenant, you can now add multitenancy support to both new and existing projects in simple steps.
- Add
CodEaisy.TinySaas.AspNetCore
to your application via Nuget or the .NET CLI
dotnet add package CodEaisy.TinySaas.AspNetCore --version 1.0.0
In
Startup.cs
, add the following inside theConfigureServices
method.public void ConfigureServices(IServiceCollection services) { // register all global singleton services here, and also dependencies for your TenantStore and ResolutionStrategy if any // ... // OPTION 1 services.AddMultiTenancy<Tenant, TenantStore<Tenant>, TenantResolutionStrategy>(); // OPTION 2 // uses default `CodEaisy.TinySaas.Model.TinyTenant` as tenant model services.AddMultiTenancy<TenantStore<TinyTenant>, TenantResolutionStrategy>(); // ... // services.AddControllers(); }
Then, add the following in the
Configure
method.public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // enable multitenant support, with missing tenant handler and tenant container // OPTION 1 // missing tenant handler has a dependency that can be provided immediately app.UseMultitenancy<Tenant, MissingTenantHandler, MissingTenantOptions>(missingTenantOptions); // OPTION 2 // missing tenant handler does not have a dependency or dependency is already registered in services app.UseMultitenancy<Tenant, MissingTenantHandler>(); // OPTION 3 // Use `SimpleTenant` as tenant model, and missing tenant handler does not have a dependency or dependency is already registered in the services app.UseMultitenancy<TMissingTenantHandler>() // ... }
In
Program.cs
, add the following in theCreateHostBuilder
method.public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) // OPTION 1: add multitenant support via TenantStartup class .ConfigureMultiTenancy<TenantStartup, Tenant>(); // OPTION 2: add multitenant support via static method .ConfigureMultiTenancy<Tenant>(ClassName.StaticMethodName);
NOTE:
Tenant
must implementCodEaisy.TinySaas.Interface
ITenant
.TenantStore
must implementCodEaisy.TinySaas.Interface.ITenantStore
.TenantResolutionStrategy
must implementCodEaisy.TinySaas.Interface.ITenantResolutionStrategy
respectively.TenantStartup
must implementIMultiTenantStartup
ClassName.StaticMethodName
must be of typeSystem.Action<TTenant, Autofac.ContainerBuilder>
whereTTenant
implementsITenant
Use Cases
TinySaas supports the most common use cases for multitenant development, and below are some examples.
Global Services
Global Services are services that do not have a core dependency on the tenant information, and they can be registered just the same way you have been used to in .NET Core, in the Startup.ConfigureServices
method.
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddSingleton<IGlobalSingletonService, GlobalSingletonService>();
services.AddScoped<IGlobalScopedService, GlobalScopedService>();
services.AddTransient<IGlobalTransientService, GlobalTransientService>();
// add multitenancy support here
services.AddControllers();
}
Tenant Services
Tenant Services are services that have core dependencies on the tenant information. TinySaas allows you to register these services like every other .NET Core service but in a tenant aware location.
Usually, tenant services will be registered either in the ConfigureServices
method of your TenantStartup
class or in the delegate passed to the IHostBuilder.ConfigureMultiTenancy
in the Program.cs
file.
public void ConfigureTenantServices(Tenant tenant, ContainerBuilder container)
{
// you can either register services using the provided container builder
// tenant singleton
container.RegisterType<TenantSingletonService>().SingleInstance();
// tenant scoped
container.RegisterType<TenantScopedService>().InstancePerRequest();
// tenant transient
container.RegisterType<TenantTransient>().InstancePerDependency();
// basically, you can use all the dependency injection features provided by Autofac on the container.
#region .NET Core DI way
// you can also do it in the well-known .NET Core default DI way
var services = new ServiceCollection();
// tenant singleton
services.AddSingleton<ITenantSingletonService, TenantSingletonService>();
// tenant scoped
services.AddScoped<ITenantScopedService, TenantScopedService>();
// tenant transient
services.AddTransient<ITenantTransientService, TenantTransientService>();
// THIS IS VERY IMPORTANT IF YOU USE THE SERVICE COLLECTION TO REGISTER ANY SERVICES HERE.
container.Populate(services);
#endregion
}
Databases
Multitenancy is not complete without databases, and TinySaas has an answer to all your needs.
Shared Database (Single Schema and Schema per Tenant)
Register your DbContext
the old way and inject IHttpContextAccessor
via your DbContext constructor.
// Startup.cs class
public void ConfigureService(IServiceCollection services)
{
//....
services.AddDbContext<AppDbContext>(...);
//...
}
// AppDbContext.cs
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options, IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
// to get the tenant information in any method
// var tenant = _httpContextAccessor.HttpContext.GetCurrentTenant<TTenant>();
}
}
`
Database Per Tenant
Simply move the DbContext
registration to the multitenant service configuration method.
public void ConfigureTenantServices(Tenant tenant, ContainerBuilder container)
{
// assume you had connectionString as a property on the tenant, you can easily do
var services = new ServiceCollection();
services.AddDbContext<AppDbContext>(tenant.ConnectionString);
container.Populate(services);
}
Other Concerns
Options
can also be registered the same way as discussed for services and databases.
You can check out the source repo for TinySaas on Github, and give feedback via the issues tab.