以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 多租户的实现方案的更多相关文章

  1. 多租户SaaS平台的数据库方案

    1.1 多租户是什么 多租户技术(Multi-TenancyTechnology)又称多重租赁技术:是一种软件架构技术,是实现如何在多用户环境下 (此处的多用户一般是面向企业用户)共用相同的系统或程序 ...

  2. 实现saas多租户方案比较

    看到一篇比较多租户数据隔离方案的文章,总结挺不错.其实大部分内容在我前几年写的文章都有. 文章翻译自: https://blog.arkency.com/comparison-of-approache ...

  3. 数据层的多租户浅谈(SAAS多租户数据库设计)

    在上一篇“浅析多租户在 Java 平台和某些 PaaS 上的实现”中我们谈到了应用层面的多租户架构,涉及到 PaaS.JVM.OS 等,与之相应的是数据层也有多租户的支持. 数据层的多租户综述 多租户 ...

  4. EF Core 实现多租户

    目录 SAAS 和多租户 多租户数据隔离方案 使用 EF Core 简单实现多租户 单数据库实现 多数据库实现 源代码 参考 SAAS 和多租户 SaaS(软件及服务)区别于其他应用程序的主要特征就是 ...

  5. 在 Windows Azure 上设计多租户应用程序

    作者:Suren Machiraju 和 Ralph Squillace 审校:Christian Martinez.James Podgorski.Valery Mizonov 和 Michael ...

  6. [转载]数据层的多租户浅谈(SAAS多租户数据库设计)

    原文:http://www.ibm.com/developerworks/cn/java/j-lo-dataMultitenant/index.html 在上一篇“浅析多租户在 Java 平台和某些 ...

  7. 一种Django多租户解决方案

    什么是多租户? 多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(此处的多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性. 多租 ...

  8. 如何在多模型的情况下进行EF6的结构迁移

    所谓多模型就是在一个数据库中包含两个不同模型,或者换句话说就是两个不同DbContext的数据都放到同一个数据库中.这里的多模型不是指多租户的数据库(有谁知道EF很好处理多租户数据库的方案,可以联系我 ...

  9. DAL – RDBMS 的分区

    编辑人员注释:本文章由AzureCAT 云与企业工程组的高级项目经理Shaun Tinline-Jones 和Chris Clayton 共同撰写. "云服务基础"应用程序也称作& ...

随机推荐

  1. base64编码是什么

    首先明确一点base64 是一种编码格式.就想utf-8一样,能在电脑上表示所有字符,或者换句话说通过编码能让电脑理解你想要标识的字符(因为电脑只知道0和1 ) 就像ascII 中 0100 0001

  2. powerdesigner mysql逆向工程注释不显示问题

  3. spring security 4.2后出现CouldnotverifytheprovidedCSRFtokenbecauseyoursessionwasnotfound

    升级到spring security 4.2后,登录不了,出现下面的错误 WARN DefaultHandlerExceptionResolver:361 - Failed to bind reque ...

  4. NOIP模拟测试1(2017081501)

    好,今天是cgg第一次举行模拟测试,希望各位支持. 时间限制:2小时 题目链接: 题目一:水得都没名字了 题目二:车站 题目三:选数 不要觉得2小时太少,我的题目很良心,都很简单. 答案可以在模拟测试 ...

  5. Django之admin管理数据库,cookie验证及分页设置

    一.admin管理数据库 1)models.py创建class类表 class Book(models.Model): name=models.CharField(max_length=) price ...

  6. 使用小技巧加快IDEA的开发速度

    一.live template的使用. 1.live template(自定义模板的载入)打开: Ctrl+shift+A 再在命令行中间输入live  template弹出用户自定义的界面.需要自行 ...

  7. 2018.10.26 洛谷P4551 最长异或路径(01trie)

    传送门 直接把每个点到根节点的异或距离插入01trie. 然后枚举每个点在01trie上匹配来更新答案就行了. 代码: #include<iostream> #include<cst ...

  8. bind函数(c++11)

    1.概念 1)c++11使用bind()函数可以向函数传递参数,一般调用形式为: 返回的newCallable是一个可调用的对象,callable是需要传参的函数,arg_list是参数列表 2)bi ...

  9. python3.4对已经存在的excel写入数据

    #!/usr/bin/env python # -*- coding:utf-8 -*- # __author__ = "blzhu" """ pyt ...

  10. idea在相应的包下右键创建servlet,找不到servlet选项

    开始pom.xml文件里面我是只引进了个servlet包,但是右键依旧没有servlet选项 <dependency> <groupId>javax.servlet</g ...