EFCore分表实现

打印 上一主题 下一主题

主题 873|帖子 873|积分 2619

实现原理

当我们new一个上下文DbContext 后, 每次执行CURD方式时 ,都会依次调用OnConfiguring(),OnModelCreating()两个方法。

  • OnConfiguring() 我们将用来替换一些服务实现,以支持分表的工作
  • OnModelCreating() 我们将用来重新实现 实体与数据库表 的映射关系
每次调用OnModelCreating()时,会判断实体与数据库表的映射关系有没有改变,如果改变则采用新的映射关系。
判断是否发生改变,通过替换 IModelCacheKeyFactory  接口的实现来完成。详情可见:在具有相同 DbContext 类型的多个模型之间进行交替
IModelCacheKeyFactory 实现

DbContextBase 是一个DbContext的实现,,ShardingRule是DbContextBase的一个共有属性。
根据分表规则的不同,每次的映射关系也会不同。
  1. public class DynamicModelCacheKeyFactoryDesignTimeSupport : IModelCacheKeyFactory
  2. {
  3.         public object Create(DbContext context, bool designTime)
  4.             => context is DbContextBase dynamicContext
  5.                 ? (context.GetType(), dynamicContext.ShardingRule, designTime)
  6.                 : (object)context.GetType();
  7.         public object Create(DbContext context)
  8.             => Create(context, false);
  9. }
复制代码
OnConfiguring() 替换接口实现
  1. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  2. {
  3.             base.OnConfiguring(optionsBuilder);
  4.             //如果分页规则有 ,代表需要分页, 那么需要替换对应的服务实现
  5.             if (!string.IsNullOrEmpty(this.ShardingRule))
  6.             {
  7.                 optionsBuilder.ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactoryDesignTimeSupport>();
  8.             }
  9. }
复制代码
ModelCustomizer 实现

在每次调用 OnModelCreating() 时,方法内部会调用实现IModelCustomizer的 ModelCustomizer.cs的Customize()方法,我们可以将映射关系写在此方法内。
通过继承实现:
IShardingTypeFinder 是一个类型查找器,请自行实现。
  1. public class ShardingModelCustomizer : ModelCustomizer
  2. {
  3.         public ShardingModelCustomizer(ModelCustomizerDependencies dependencies) : base(dependencies)
  4.         {
  5.         }
  6.         public override void Customize(ModelBuilder modelBuilder, DbContext context)
  7.         {
  8.             base.Customize(modelBuilder, context);
  9.             var dbContextBase = context as DbContextBase;
  10.             var shardingTypeFinder = dbContextBase.ServiceProvider.GetService<IShardingTypeFinder>();
  11.             //查找需要重新映射表名的类
  12.             var shardingTypes = shardingTypeFinder.FindAll(true);
  13.             if (shardingTypes != null && shardingTypes.Count() > 0)
  14.             {
  15.                 if (context is DbContextBase contextBase)
  16.                 {
  17.                     if (!string.IsNullOrEmpty(contextBase.ShardingRule))
  18.                     {
  19.                         foreach (var type in shardingTypes)
  20.                         {
  21.                             switch (contextBase.DbContextOptions.DatabaseType)
  22.                             {
  23.                                 case DatabaseType.SqlServer:
  24.                                     modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}");
  25.                                     break;
  26.                                 case DatabaseType.Sqlite:
  27.                                     modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}");
  28.                                     break;
  29.                                 case DatabaseType.MySql:
  30.                                     modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}".ToMySQLName());
  31.                                     break;
  32.                                 case DatabaseType.Oracle:
  33.                                     modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}".ToOracleName());
  34.                                     break;
  35.                                 default:
  36.                                     modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}");
  37.                                     break;
  38.                             }
  39.                         }
  40.                     }
  41.                 }
  42.             }
  43.         }
  44. }
复制代码
OnConfiguring() 替换接口实现
  1.   protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  2.   {
  3.             base.OnConfiguring(optionsBuilder);
  4.             //如果分页规则有 ,代表需要分页, 那么需要替换对应的服务实现
  5.             if (!string.IsNullOrEmpty(this.ShardingRule))
  6.             {
  7.                 optionsBuilder.ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactoryDesignTimeSupport>().ReplaceService<IModelCustomizer, ShardingModelCustomizer>();
  8.             }
  9.   }
复制代码
DbContextBase构造函数修改

上文提到了ShardingRule 这个属性的出现 , 如何给这个属性赋值呢?
有两种方式:

  • 构造函数传参
  • 通过接口获取
构造函数传参
  1. public string ShardingRule { get; set; }
  2. public DbContextBase(string shardingRule, DbContextOptions options) : base(options)
  3. {
  4.             ShardingRule = shardingRule;
  5. }
复制代码
通过接口获取

IShardingRule是实现规则名称的自定义接口,自行实现
  1. protected DbContextBase(DbContextOptions options, IServiceProvider serviceProvider)
  2.           : base(options)
  3. {
  4.             ShardingRule = (serviceProvider.GetService<IShardingRule>()).GetValue();
  5. }
复制代码
使用方式

这里只介绍构造函数传参使用方式
  1. DbContextOptionsBuilder<DbContextBase> optionsBuilder = new DbContextOptionsBuilder<DbContextBase>();
  2. optionsBuilder.UseSqlServer("connStr");
  3. var options =  optionsBuilder.Options;
  4. using (var dbContext = new DbContextBase("202209", options))
  5. {
  6.         //TODO....
  7.             
  8. }
复制代码
跨上下文使用事务

这里需要注意的是,跨上下文使用事务必须使用同一个连接,所以optionsBuilder.UseSqlServer(connection);这里的写法改变一下,使用同一连接
  1.             DbContextOptionsBuilder<DbContextBase> optionsBuilder = new DbContextOptionsBuilder<DbContextBase>();
  2.             IDbConnection connection = new SqlConnection("connStr");
  3.             optionsBuilder.UseSqlServer(connection);
  4.             var options =  optionsBuilder.Options;
  5.             using (var dbContext = new DbContextBase("202209", options))
  6.             {
  7.                 using (var  transaction =await dbContext.Database.BeginTransactionAsync())
  8.                 {
  9.                     using (var dbContext2 = new DbContextBase("202210", options))
  10.                     {
  11.                         await dbContext2.Database.UseTransactionAsync(transaction);
  12.                         //TODO....
  13.                         transaction.Commit();
  14.                     }
  15.                 }
  16.                
  17.             }
复制代码
总结

EFCore分表的实现大致全是这样,没有什么区别。可以参考一些开源的框架,对现有的系统进行适当的调整,毕竟别人写的并不一定适合你。希望这篇文章可以帮到你。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

飞不高

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表