前言
来到这篇随笔,我们继续演示如何实现EF多租户。
今天主要是演示多租户下的变形,为下图所示
实施
项目结构
这次我们的示例项目进行了精简,仅有一个API项目,直接包含所有代码。
其中Controller,StoreContext,Entity都完全和以往的示例一模一样,这里就不再过多介绍了。
具有主要区别的是 CombinedConnectionGenerator 和 Startup
代码解释
1. 首先要关注的是作为入口的 Startup ,还是一个套路,分别在 ConfigureService 注册EF多租户, 在 Configure 配置中间件。
ConfigureService 还是一贯的简单。但是注意这里使用的 AddMySqlPerTable 这个模式。
在混合的模式中,需要已最小的单元作为服务注册。由于这次是数据库和数据表混合模式,所以需要用数据表来注册。
1 public void ConfigureServices(IServiceCollection services) 2 { 3 services.AddScoped(); 4 services.AddMySqlPerTable (settings => 5 { 6 settings.ConnectionPrefix = "mysql_"; 7 }); 8 services.AddControllers(); 9 }
Configure的使用更加简单,只需要添加中间件 TenantInfoMiddleware 即可。
1 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 2 { 3 if (env.IsDevelopment()) 4 { 5 app.UseDeveloperExceptionPage(); 6 } 7 8 app.UseMiddleware(); 9 10 app.UseRouting(); 11 12 app.UseEndpoints(endpoints => 13 { 14 endpoints.MapControllers(); 15 }); 16 }
2. 这次需要自己实现 ConnectionGenerator
关键点有2个,
第一个关键点,由于我们的类库是同时支持多个DbContext的,所以需要有TenantKey去区分。由于有种特殊情况,需要一个 ConnectionGenerator 同时支持多个 DbContext ,所以这里提供了 MatchTenantKey 方法作为补充的判断依据。
可以看出来,我们这里TenantKey 为空,所以一般都不会匹配中。示例中完全是依靠 MatchTenantKey 来做匹配的。
第二个关键点,GetConnection 作为最主要的逻辑方法,通过对TenantName 的数字部分进行取模,最终拼接处ConnectionString的键值
并且通过 Configuration 获取连接字符串
1 public class CombindedConnectionGenerator : IConnectionGenerator 2 { 3 private readonly IConfiguration configuration; 4 public string TenantKey => ""; 5 6 public CombindedConnectionGenerator(IConfiguration configuration) 7 { 8 this.configuration = configuration; 9 } 10 11 12 public string GetConnection(TenantOption option, TenantInfo tenantInfo) 13 { 14 var span = tenantInfo.Name.AsSpan(); 15 if (span.Length > 4 && int.TryParse(span[5].ToString(), out var number)) 16 { 17 return configuration.GetConnectionString($"{option.ConnectionPrefix}container{number % 2 + 1}"); 18 } 19 throw new NotSupportedException("tenant invalid"); 20 } 21 22 public bool MatchTenantKey(string tenantKey) 23 { 24 return true; 25 } 26 }
检验结果
检验结果我觉得已经没有必要的,都是同样的套路,主要的区别是,之前的只有一个数据库,或者多个数据库
这次的混合模式,主要是一个数据库作为一个container,里面可以同时包含多个product数据表。
Container1
Container2
总结
其实这个例子也是非常简单的,目的是让每个人都能快速应用复杂的分库分表
下一篇文章将会通过多租户实现读写分离。
关于这个文章的所有代码,已经同步到Github
https://github.com/woailibain/kiwiho.EFcore.MultiTenant/tree/master/example/mix_mode/kiwiho.EFcore.MultiTenant.MixMode