ServiceStack 多租户的实现方案
以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();
});
}
}
下面就到核心代码了,在主程序中建立多租户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(Container container)
{
ConigureSqlserver(container);
} private void ConigureSqlserver(Container container)
{
var dbFactory = new OrmLiteConnectionFactory(
"Data Source=.; Initial Catalog=TMaster;User Id=TMaster;Password=t123;pooling=true;", SqlServerDialect.Provider); const int noOfTennants = ; container.Register<IDbConnectionFactory>(c =>new MultiTenantDbFactory(dbFactory)); var multiDbFactory = (MultiTenantDbFactory)container.Resolve<IDbConnectionFactory>(); using (var db = multiDbFactory.OpenTenant())
InitDb(db, "TMaster", "Masters inc."); for(int i=; i<= noOfTennants; i++)
{
var tenantId = $"T{i}"; using (var db = multiDbFactory.OpenTenant(tenantId))
InitDb(db, tenantId, $"ACME {tenantId} inc.");
} GlobalRequestFilters.Add((req, res, dto) =>
{
var forTennant = dto as IForTenant;
if (forTennant != null)
RequestContext.Instance.Items.Add("TenantId", forTennant.TenantId);
});
}
public void InitDb(IDbConnection db, string tenantId, string company)
{
db.DropAndCreateTable<TenantConfig>();
db.Insert(new TenantConfig { Id = tenantId, Company = company });
}
这样核心代码就完成了,我们用postman调用试试看,是不是达到了预期的效果
body为空,租户Id没有设置,系统认为是默认的数据库TMaster,返回的是Master数据库中的config表信息

body中设置json参数{"name":"joy", "tenantId":"t1"},可以看到查询返回的是数据库T1的信息

我们再试验一下T2,body中设置json参数{"name":"peter", "tenantId":"t2"}

可以很惊喜的看到,查询的是数据库T2的信息
ServiceStack解决方案真是强大,本来一个复杂的多租户问题就这样轻易解决了,是不是很简单。这里例子用的都是sqlserver数据库,实际上每个租户可以使用不同的数据库。
最近一年很流行ABP解决方案,我想说的是ServiceStack解决方案也很优秀,甚至更加优秀,当你了解越多你就会惊叹当初作者的设计思路是多么的优秀,有兴趣的小伙伴可以一起挖掘和分享啊!
ServiceStack 多租户的实现方案的更多相关文章
- 多租户SaaS平台的数据库方案
1.1 多租户是什么 多租户技术(Multi-TenancyTechnology)又称多重租赁技术:是一种软件架构技术,是实现如何在多用户环境下 (此处的多用户一般是面向企业用户)共用相同的系统或程序 ...
- 实现saas多租户方案比较
看到一篇比较多租户数据隔离方案的文章,总结挺不错.其实大部分内容在我前几年写的文章都有. 文章翻译自: https://blog.arkency.com/comparison-of-approache ...
- 数据层的多租户浅谈(SAAS多租户数据库设计)
在上一篇“浅析多租户在 Java 平台和某些 PaaS 上的实现”中我们谈到了应用层面的多租户架构,涉及到 PaaS.JVM.OS 等,与之相应的是数据层也有多租户的支持. 数据层的多租户综述 多租户 ...
- EF Core 实现多租户
目录 SAAS 和多租户 多租户数据隔离方案 使用 EF Core 简单实现多租户 单数据库实现 多数据库实现 源代码 参考 SAAS 和多租户 SaaS(软件及服务)区别于其他应用程序的主要特征就是 ...
- 在 Windows Azure 上设计多租户应用程序
作者:Suren Machiraju 和 Ralph Squillace 审校:Christian Martinez.James Podgorski.Valery Mizonov 和 Michael ...
- [转载]数据层的多租户浅谈(SAAS多租户数据库设计)
原文:http://www.ibm.com/developerworks/cn/java/j-lo-dataMultitenant/index.html 在上一篇“浅析多租户在 Java 平台和某些 ...
- 一种Django多租户解决方案
什么是多租户? 多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(此处的多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性. 多租 ...
- 如何在多模型的情况下进行EF6的结构迁移
所谓多模型就是在一个数据库中包含两个不同模型,或者换句话说就是两个不同DbContext的数据都放到同一个数据库中.这里的多模型不是指多租户的数据库(有谁知道EF很好处理多租户数据库的方案,可以联系我 ...
- DAL – RDBMS 的分区
编辑人员注释:本文章由AzureCAT 云与企业工程组的高级项目经理Shaun Tinline-Jones 和Chris Clayton 共同撰写. "云服务基础"应用程序也称作& ...
随机推荐
- c#对dataset和list集合压缩和解压,能提高访问速度
public class YS { public static byte[] Decompress(byte[] data) { byte[] bData; MemoryStream ms = new ...
- sql条件查询-日期比较(取年月部分)
查询当年当月的数据: select * from compalete_task where to_Char(create_date,'yyyyMM') = to_Char(sysdate,'yyyyM ...
- putty中查询乱码问题
我们在putty连接Linux时候,有时候查询会出现乱码问题...如下图 这个是因为putty中设置编码字符集的原因..将此换为utf8格式的即可解决 解决后查询如下:
- Java环境编写
首先安装jdk,本系统中jdk安装在D:\jdk:jre安装在D:\Jre: 然后开始配置环境变量: JAVA_HOME:D:\jdk; JRE_HOME:D:\jre; CLASSPATH:.;%J ...
- 第一个C#语言
第一个C#程序 .NET和C#的区别 1. C#只是.NET的一部分,.NET不止包含C# 2. C#是一种程序语言,.NET是一个平台.框架 IDE环境:vs 2012 VS2012的窗口结构 ...
- kbmmw 与extjs 通过JSON Base64 显示图片
delphi 官网上开始也来越多的介绍delphi与extjs 结合的例子,今天我就把官方的例子翻版一下. 官方使用C++builder 和webbroker 实现. 我使用kbmmw 来实现一下. ...
- 关于Code Review
为了保证代码质量,我们团队内部一直在推行Code Review.现在Code Review帮我们发现了很多代码的问题,提升了代码的可读性和质量,同时我们在Code Review上也花费了很多时间,有些 ...
- mysql 5.7 修改密码
mysql 5.7 ,user表就没有password 这个字段了. ') where user='root' and host='localhost'; 这样当然就改不了密码了. ') where ...
- Mysql导入excel数据,解决某些特殊字符乱码问题
问题 做项目需要从excel表格导入到mysql的数据库表中,excel表格中的“规格”字段的“×”符号导入数据库表中,会出现部分数据的“×”这个符号会乱码,成“?”的形式. 解决方法 打开excel ...
- TP5在lnmp环境中不能重写的问题
说到坑,这个问题困扰了我一两天时间,本地可以,线上环境检查了好久. 基本检查的地方有几个了,首先就是nginx下面的重写配置,这个大家在网上都能搜到,至于定义的变量和配置路径,修改一下即可. 还有就是 ...