Magicodes.WeiChat——多租户的设计与实现
概要
多租户(Multi Tenancy/Tenant)是一种软件架构,其定义是:在一台服务器上运行单个应用实例,它为多个租户提供服务。
本框架使用的是共享数据库、共享 Schema、共享数据表的数据设计架构。
操作说明
进入系统管理员界面,打开租户管理界面,如下图所示:

下面是租户管理界面:

这里可以管理租户成员,也可以让管理员绑定微信。
下面是公众号配置界面:

这里可以配置公众号的信息。
系统管理员不仅可以管理自己的租户,还可以管理其他租户内容——公众号管理。
下面是公众号管理界面:

架构实现
如上面所述,本框架使用的是共享数据库、共享 Schema、共享数据表的数据设计架构。那么,本框架是如何实现的呢?
主要是分为以下三步:
1. 建立TenantId
2. 扩展ASP.NET Indentity以支持多租户
3. 注册租户筛选器
那么首先,这里需要介绍的是TenantId。
建立租户Id(TenantId)
我们先来看看租户表:
/// <summary>
/// 租户信息
/// </summary>
public class Account_Tenant
{
/// <summary>
/// 多租户Id
/// </summary>
public int Id { get; set; }
/// <summary>
/// 是否为系统租户(仅支持一个)
/// </summary>
public bool IsSystemTenant { get; set; }
/// <summary>
/// 租户名称
/// </summary>
[Display(Name = "名称")]
[Required]
[MaxLength()]
public string Name { get; set; }
[Display(Name = "备注")]
[DataType(DataType.MultilineText)]
[MaxLength()]
public string Remark { get; set; }
}
如上所示,Id为主键,标识列,由数据库自动生成(EF Code First模式下,默认Id为主键,int类型主键自动设置为标识列)。
那么,租户Id产生了之后,所有租户共享数据表存放数据,不同租户的数据需要通过 TenantId 字段来区分。
我们来看一个基类的设计:
public abstract class WeiChat_TenantBase<TKey> : ITenantId, IAdminCreate<string>, IAdminUpdate<string>
{
[Key]
public virtual TKey Id { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[Display(Name = "创建时间")]
public DateTime CreateTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
[Display(Name = "更新时间")]
public DateTime? UpdateTime { get; set; }
/// <summary>
/// 创建者
/// </summary>
[MaxLength()]
public string CreateBy { get; set; } /// <summary>
/// 创建者
/// </summary>
[Display(Name = "创建者")]
//[NotMapped]
[ForeignKey("CreateBy")]
public AppUser CreateUser { get; set; } /// <summary>
/// 更新者
/// </summary>
[MaxLength()]
public string UpdateBy { get; set; }
/// <summary>
/// 编辑者
/// </summary>
[MaxLength()]
[Display(Name = "最后编辑")]
//[NotMapped]
public AppUser UpdateUser { get; set; } public int TenantId { get; set; }
}
如上所示,TenantId就是数据的分水岭,不同数据的筛选需要根据其来筛选。
如下图的设计:

从上图可以看出,这块错综复杂的类都缺不了TenantId,可能看类还是不太明白,我们来看表结构吧,比如说:




等等。如上面表结构所示,TenantId为个表间必备字段。
而在Code First模式下,使用继承可以很方便的将所有的模型类加上相关字段。
众所周知,本框架使用了ASP.NET Indentity,那么如何对ASP.NET Indentity实现多租户的扩展呢?
扩展ASP.NET Indentity以支持多租户
在本框架中,编写了库Magicodes.WeiChat.Data.Multitenant,用于扩展ASP.NET Indentity以支持多租户。
使用过ASP.NET Indentity的朋友应该都知道Microsoft.AspNet.Identity.EntityFramework——ASP.NET Indentity使用EF作为其数据存储的实现库。通过对象浏览器查看,不难看出,其主要定义了以下对象:

其中,IdentityDbContext 继承自System.Data.Entity.DbContext,具体定义如下所示:
public class IdentityDbContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim> : System.Data.Entity.DbContext
where TUser : Microsoft.AspNet.Identity.EntityFramework.IdentityUser<TKey, TUserLogin, TUserRole, TUserClaim>
where TRole : Microsoft.AspNet.Identity.EntityFramework.IdentityRole<TKey, TUserRole>
where TUserLogin : Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin<TKey>
where TUserRole : Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole<TKey>
where TUserClaim : Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim<TKey>
Microsoft.AspNet.Identity.EntityFramework 的成员
那么,我们现在的主要对象就是搞定她们了。

一一对应关系如下所示:

如上所示,通过扩展ASP.NET Identity的IUser、IdentityUser、IdentityDbContext、IdentityUserLogin、UserStore来完成了对多租户的支持,同时需要注意的是,还要重写方法FindByNameAsync、AddLoginAsync、FindAsync、FindByEmailAsync、CreateAsync,以支持多租户。
完成了对ASP.NET Identity的多租户的支持,我们还需要对数据进行筛选,但是所有地方都添加筛选代码是一件很麻烦的事情,而且在编写逻辑的时候还很容易健忘,那么有什么好的方式呢?是时候祭出我们的神器了——EntityFramework.DynamicFilters。
注册租户筛选器
筛选器依赖ENTITYFRAMEWORK.DYNAMICFILTERS,这是一个开源项目,相关介绍可以访问以下链接:
https://github.com/jcachat/EntityFramework.DynamicFilters
这里,我们定义了如下租户筛选器:
modelBuilder.Filter("TenantEntryFilter", (ITenantId app, int tenantId) => (app.TenantId == tenantId), 0);
然后我们可以使用以下代码来启用筛选器:
db.EnableFilter(tenantFilterName);
//设置多租户过滤
db.SetFilterScopedParameterValue(tenantFilterName, "tenantId", TenantId);
以上代码大家可以写到通用的地方进行封装,比如控制器基类的OnActionExecuting方法中。
尾声
至此,整个多租户的架构就基本完成了。当然我们还可以进行扩展,比如实现租户缓存、租户资源管理等等,这是后续的话题了。
Magicodes.WeiChat——多租户的设计与实现的更多相关文章
- Magicodes.WeiChat——V3.0(多租户)版本发布
主要内容如下: 添加项目Magicodes.WeiChat.Data.Multitenant,全面支持多租户(基于EF已经ASP.NET Identity) 增加租户管理.租户成员管理.修改密码.公众 ...
- Magicodes.WeiChat——版本发布历史
购买地址:https://item.taobao.com/item.htm?id=520205558575 您可以在新标签页打开此图,以查看原始图片. Magicodes.WeiChat为湖南心莱信息 ...
- Magicodes.WeiChat——WeChatOAuthTest(网页授权获取用户基本信息)
Demo访问地址:http://wechat.magicodes.net/app/AppDemo/WeChatOAuthTest?tenantId=1 关于公众号如何获取用户信息,请参考此文档:htt ...
- Magicodes.WeiChat——ASP.NET Scaffolding生成增删改查、分页、搜索、删除确认、批量操作、批量删除等业务代码
关于T4代码生成这块,我之前写过几篇帖子,如:<Magicodes.NET框架之路——让代码再飞一会(ASP.NET Scaffolding)>(http://www.cnblogs.co ...
- Magicodes.WeiChat——缓存管理
本框架支持缓存管理,内部机制使用开源库CacheManager.支持全局缓存.租户缓存,默认使用的系统缓存实现,可以在Web.config将其配置为其他缓存类型,比如支持Redis.内存等. 开源库地 ...
- Magicodes.WeiChat——自定义knockoutjs template、component实现微信自定义菜单
本人一向比较喜欢折腾,玩了这么久的knockoutjs,总觉得不够劲,于是又开始准备折腾自己了. 最近在完善Magicodes.WeiChat微信开发框架时,发现之前做的自定义菜单这块太不给力了,而各 ...
- Magicodes.WeiChat——利用纷纭打造云日志频道
纷纭,是个免费的渠道集成工具.这里我就不多介绍了,右侧是飞机票:https://lesschat.com/ 在开发或者在运维情况下,我们经常需要查看并关注服务器端日志以确保程序是否健康运行.尤其是在微 ...
- Magicodes.WeiChat——使用AntiXssAttribute阻止XSS(跨站脚本攻击)攻击
跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS.恶意攻击者往Web页面里插 ...
- Magicodes.WeiChat——后台JS框架封装
Magicodes.WeiChat同时也致力于提高后台开发效率,因此对在后台前端这块也做了一定的封装.我们先来说说主要的框架JS——mwc.js和mwc_elements.js.这两个JS文件位于Sc ...
随机推荐
- sql条件中比较性能优化
第一个比第二个性能高. 查询语句意义: 如果codelist中tablecode配置为0时, t.Table_Code = 'SV_RETURN_BILL'不生效. 如果codelist中tablec ...
- web语义化与h5新增标签
Web语义化就是html告诉我们也告诉机器这一块是什么内容,例如:“这行是一个标题,这几行组成一个段落,这是一个列表,那是一个链接.” Web语义化有三个阶段: 1.h1~h6.thead.ul. ...
- python json学习之路2-认识python种的json模块
1.从python原始类型向json类型的转化过程,具体的转化对照如下: 2.从json到python的类型转化对照如下: 3.json提供四个功能:dumps, dump, loads, load ...
- 论垃圾邮件危害性及U-Mail邮件系统必杀技
阿里集团今年“双十一电商节”又一次突破了去年营收,创造了新的历史.相信在电商日益渗入生 活的今天,你在日常工作中一定收到过某店铺发来的推广邮件,的确,邮件如今被电商广泛应用于消费者购物各环节,但是在其 ...
- 邮件江湖群狼环伺 U-Mail邮件系统防狼有术
小时候听过一首儿歌<小兔子乖乖>,里面说到有条恶狼,常常冒充小兔子的“妈妈”,要求小兔 子开门,但小兔子谨守妈妈的训诫,就是不开门,直到辨别出妈妈在窗外的声音,才打开房门.如果我们将一些似 ...
- scala 学习心得
scala 安装步骤 文件下载地址:www.scala-lang.org(Please report bugs at https://issues.scala-lang.org/. We welcom ...
- SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null' 解决办法
当跑jndi项目时抛出:org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class '' fo ...
- centos搭建NFS网络文件系统
NFS服务器端 安装NFS服务器非常之简单: yum install nfs-utils protmap 这样就安装好了,其中nfs-utils是提供NFS服务器程序和相应的管理工具.protmap是 ...
- XML Xpath学习
Xpath是一门在xml文档中查找信息的语言. Xpath可用来在xml文档中对元素和属性进行遍历. <1>路径表达式1: 斜杠(/)作为路径内部的分隔符 同一个路径有绝对路径和相对路径两 ...
- python学习之——计算给出代码中注释、代码、空行的行数
题目:计算给出代码中注释.代码.空行的行数 来源:网络 思路:注释行以 ‘#’开头,空行以 ‘\n’ 开头,以此作为判断 def count_linenum(fname): fobj = open(f ...