以SqlServer为例子说明ServiceStack实现多租户,在SqlServer中创建4个Database:TMaster、T1,T2,T3,为了安全起见
每个Database不用sa账号,而是用独立的数据库的账号和密码,为了方便演示这密码设置成一样
租户TMaster Database:TMaster 账号密码: User Id=TMaster;Password=t123
租户T1 Database:T1 账号密码: User Id=T1;Password=t123
租户T2 Database:T2 账号密码: User Id=T2;Password=t123
租户T3 Database:T3 账号密码: User Id=T3;Password=t123
创建数据库的方法可以参见文章: https://www.cnblogs.com/tonge/p/3791029.html
每个登陆用自己的账号和密码登陆,其它的数据库是没有访问权限的,这个各个租户是完全隔离的。
假设Node和npm已经安装
npm install -g @servicestack/cli
执行命令dotnet-new selfhost SSHost
这样就创建了ServiceStack的控制台程序,用VS2017解决方案,在ServiceModel的Types文件夹添加TenantConfig类文件
代码如下:
using System; using System.Collections.Generic; using System.Text; namespace ssTest.ServiceModel.Types { public interface IForTenant { string TenantId { get; } } public class TenantConfig { public string Id { get; set; } public string Company { get; set; } } }
修改Hello.cs文件,代码如下:
using ServiceStack; using ssTest.ServiceModel.Types; using System; namespace ssTest.ServiceModel { [Route("/hello")] [Route("/hello/{Name}")] public class Hello : IForTenant, IReturn<HelloResponse> { public string Name { get; set; } // 实现接口IForTenant(租户标识Id) public string TenantId { get; set; } } public class HelloResponse { public string Result { get; set; } public DateTime Date { get; set; } // 返回租户公司信息 public TenantConfig Config { get; set; } } }
主程序的Startup代码如下
public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // JsConfig.DateHandler = DateHandler.ISO8601; // 保证时间类型的字段可以解析成js识别的时间类型 JsConfig<DateTime>.SerializeFn = time => new DateTime(time.Ticks, DateTimeKind.Local).ToString("o"); JsConfig<DateTime?>.SerializeFn = time => time != null ? new DateTime(time.Value.Ticks, DateTimeKind.Local).ToString("o") : null; JsConfig.DateHandler = DateHandler.ISO8601; app.UseServiceStack(new AppHost()); app.Run(context => { context.Response.Redirect("/metadata"); return Task.FromResult(0); }); } }
下面就到核心代码了,在主程序中建立多租户Db工程类,让程序可以自动的根据租户Id访问自己的数据库
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 OpenTenant(tenantId); } public IDbConnection OpenTenant(string tenantId = null) { return tenantId != null ? dbFactory.OpenDbConnectionString($"Data Source=.; Initial Catalog={tenantId};User Id={tenantId};Password=t123;pooling=true;") : dbFactory.OpenDbConnection(); } public IDbConnection CreateDbConnection() { return dbFactory.CreateDbConnection(); } }
AppHost中加入如下代码,GlobalRequestFilters的作用,根据传入的租户Id来选择相应的数据库,如果租户Id为null,系统自动使用TMaster数据库
InitDb的作用就是初始化三个数据库,创建表TenantConfig并插入一条记录。
public override void Configure(Contai