I've added an example Multi Tenant test showing my preferred approach where I would use a custom IDbConnectionFactory wrapper so I have better visibility and control of the Db Connections being created, i.e:
public class MultiTenantDbFactory : IDbConnectionFactory
{
private readonly IDbConnectionFactory dbFactory;
public MultiTenantDbFactory(IDbConnectionFactory dbFactory)
{
this.dbFactory = dbFactory;
}
public IDbConnection OpenDbConnection()
{
var tenantId = RequestContext.Instance.Items["TenantId"] as string;
return tenantId != null
? dbFactory.OpenDbConnectionString(GetConnectionString(tenantId))
: dbFactory.OpenDbConnection();
}
public IDbConnection CreateDbConnection()
{
return dbFactory.CreateDbConnection();
}
}
I'll also prefer to have a master dbFactory singleton instance to use as a default for non-tenant requests which also specifies which Dialect Provider to use:
var dbFactory = new OrmLiteConnectionFactory(
AppSettings.GetString("MasterDb"), SqlServerDialect.Provider);
container.Register<IDbConnectionFactory>(c =>
new MultiTenantDbFactory(dbFactory));
To indicate that a Service is tenant-specific, I'll just create a custom interface:
public interface IForTenant
{
string TenantId { get; }
}
Which Request DTO's can implement to indicate they're tenant-specific requests, i.e:
public class GetTenant : IForTenant, IReturn<GetTenantResponse>
{
public string TenantId { get; set; }
}
Which can be easily detected throughout ServiceStack's Request pipeline like a Global Request Filter to pull out what tenant the request is for and add it to the RequestContext, e.g:
GlobalRequestFilters.Add((req, res, dto) =>
{
var forTennant = dto as IForTenant;
if (forTennant != null)
RequestContext.Instance.Items.Add("TenantId", forTennant.TenantId);
});
The MultiTenantDbFactory can then read this back and open the Db Connection to the desired tenant:
var tenantId = RequestContext.Instance.Items["TenantId"] as string;
return new OrmLiteConnectionFactory(GetConnectionStringFor(tenantId))
.OpenDbConnection()
Which will be used whenever anyone accesses base.Db in their services or dependencies.