Magicodes.WeiChat——多租户的设计与实现

简介: 概要 多租户(Multi Tenancy/Tenant)是一种软件架构,其定义是:在一台服务器上运行单个应用实例,它为多个租户提供服务。 本框架使用的是共享数据库、共享 Schema、共享数据表的数据设计架构。

概要

多租户(Multi Tenancy/Tenant)是一种软件架构,其定义是:在一台服务器上运行单个应用实例,它为多个租户提供服务。

本框架使用的是共享数据库、共享 Schema、共享数据表的数据设计架构。

操作说明

进入系统管理员界面,打开租户管理界面,如下图所示:

image

下面是租户管理界面:

image

这里可以管理租户成员,也可以让管理员绑定微信。

下面是公众号配置界面:

image

这里可以配置公众号的信息。

系统管理员不仅可以管理自己的租户,还可以管理其他租户内容——公众号管理。

下面是公众号管理界面:

image

架构实现

如上面所述,本框架使用的是共享数据库、共享 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(20)]
        public string Name { get; set; }
        [Display(Name = "备注")]
        [DataType(DataType.MultilineText)]
        [MaxLength(500)]
        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(128)]
        public string CreateBy { get; set; }

        /// <summary>
        /// 创建者
        /// </summary>
        [Display(Name = "创建者")]
        //[NotMapped]
        [ForeignKey("CreateBy")]
        public AppUser CreateUser { get; set; }

        /// <summary>
        /// 更新者
        /// </summary>
        [MaxLength(128)]
        public string UpdateBy { get; set; }
        /// <summary>
        /// 编辑者
        /// </summary>
        [MaxLength(256)]
        [Display(Name = "最后编辑")]
        //[NotMapped]
        public AppUser UpdateUser { get; set; }

        public int TenantId { get; set; }
}

如上所示,TenantId就是数据的分水岭,不同数据的筛选需要根据其来筛选。

如下图的设计:

image

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

imageimageimageimage

等等。如上面表结构所示,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作为其数据存储的实现库。通过对象浏览器查看,不难看出,其主要定义了以下对象:

image

其中,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 的成员

那么,我们现在的主要对象就是搞定她们了。

image

一一对应关系如下所示:

image

如上所示,通过扩展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方法中。

尾声

至此,整个多租户的架构就基本完成了。当然我们还可以进行扩展,比如实现租户缓存、租户资源管理等等,这是后续的话题了。

目录
相关文章
|
7月前
|
开发者 前端开发 Java
构建多租户应用程序:深入探讨Entity Framework Core中的租户支持策略与实现
【8月更文挑战第31天】Vaadin 是一个流行的 Java Web 框架,提供丰富的 UI 组件库,助力开发者快速构建美观且功能强大的 Web 应用。本文深入探讨 Vaadin 组件库,介绍如何基于功能性、可访问性、性能和可定制性选择合适的组件,并提供示例代码,帮助开发者做出明智决策。无论是简单的输入框还是复杂的表格,Vaadin 都能满足各种需求。
63 0
Micro Framework Interop功能实现
目前.Net Micro Framework仅支持C#语言开发应用程序,由于是托管代码,垃圾回收随时都可能发生,其实时性很难得到保证,所以有windows开发经验的用户就会有这种想法:.Net Micro Framework能否支持非托管代码,也就是所谓的P/Invoke平台调用功能
878 0
|
前端开发 .NET 开发框架
|
测试技术 Android开发 iOS开发
使用Xamarin.Forms的企业应用程序模式(电子书)--介绍
无论平台如何,企业应用开发人员都面临着几个挑战: 随时间变化的应用程序要求。 新的商机和挑战。 开发期间持续的反馈可能会显着影响应用程序的范围和要求。
1474 0
|
存储 API 容器
使用Xamarin.Forms的企业应用程序模式(电子书)--配置管理
设置允许将配置应用程序行为的数据与代码分离,允许在不重新构建应用程序的情况下更改行为。有两种类型的设置:应用设置和用户设置。 应用设置是应用程序创建和管理的数据。它可以包括固定Web服务端点,API密钥和运行时状态等数据。
1103 0
|
存储 前端开发 数据可视化
使用Xamarin.Forms的企业应用程序模式(电子书)--MVVM
XAMarin.Forms的开发人员经验通常涉及在XAML中创建用户界面,然后添加在用户界面上运行的代码隐藏。 随着应用程序的修改和扩展的规模和范围,可能会出现复杂的维护问题。 这些问题包括UI控件和业务逻辑之间的紧密耦合,这增加了UI修改的成本,以及单元测试这些代码的难度。
1107 0