EFCore分表实现

实现原理

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

  • OnConfiguring() 我们将用来替换一些服务实现,以支持分表的工作
  • OnModelCreating() 我们将用来重新实现 实体与数据库表 的映射关系

每次调用OnModelCreating()时,会判断实体与数据库表的映射关系有没有改变,如果改变则采用新的映射关系。

判断是否发生改变,通过替换 IModelCacheKeyFactory 接口的实现来完成。详情可见:在具有相同 DbContext 类型的多个模型之间进行交替

IModelCacheKeyFactory 实现

DbContextBase 是一个DbContext的实现,,ShardingRuleDbContextBase的一个共有属性。
根据分表规则的不同,每次的映射关系也会不同。

 public class DynamicModelCacheKeyFactoryDesignTimeSupport : IModelCacheKeyFactory  {         public object Create(DbContext context, bool designTime)             => context is DbContextBase dynamicContext                 ? (context.GetType(), dynamicContext.ShardingRule, designTime)                 : (object)context.GetType();          public object Create(DbContext context)             => Create(context, false);   } 

OnConfiguring() 替换接口实现

 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  {             base.OnConfiguring(optionsBuilder);             //如果分页规则有 ,代表需要分页, 那么需要替换对应的服务实现             if (!string.IsNullOrEmpty(this.ShardingRule))             {                 optionsBuilder.ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactoryDesignTimeSupport>();              }  } 

ModelCustomizer 实现

在每次调用 OnModelCreating() 时,方法内部会调用实现IModelCustomizerModelCustomizer.csCustomize()方法,我们可以将映射关系写在此方法内。

通过继承实现:
IShardingTypeFinder 是一个类型查找器,请自行实现。

public class ShardingModelCustomizer : ModelCustomizer {          public ShardingModelCustomizer(ModelCustomizerDependencies dependencies) : base(dependencies)         {         }          public override void Customize(ModelBuilder modelBuilder, DbContext context)         {             base.Customize(modelBuilder, context);             var dbContextBase = context as DbContextBase;             var shardingTypeFinder = dbContextBase.ServiceProvider.GetService<IShardingTypeFinder>();             //查找需要重新映射表名的类             var shardingTypes = shardingTypeFinder.FindAll(true);              if (shardingTypes != null && shardingTypes.Count() > 0)             {                  if (context is DbContextBase contextBase)                 {                     if (!string.IsNullOrEmpty(contextBase.ShardingRule))                     {                         foreach (var type in shardingTypes)                         {                             switch (contextBase.DbContextOptions.DatabaseType)                             {                                 case DatabaseType.SqlServer:                                     modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}");                                     break;                                 case DatabaseType.Sqlite:                                     modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}");                                     break;                                 case DatabaseType.MySql:                                     modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}".ToMySQLName());                                     break;                                 case DatabaseType.Oracle:                                     modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}".ToOracleName());                                     break;                                 default:                                     modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}");                                     break;                             }                         }                     }                 }              }            }  } 

OnConfiguring() 替换接口实现

  protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)   {             base.OnConfiguring(optionsBuilder);             //如果分页规则有 ,代表需要分页, 那么需要替换对应的服务实现             if (!string.IsNullOrEmpty(this.ShardingRule))             {                 optionsBuilder.ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactoryDesignTimeSupport>().ReplaceService<IModelCustomizer, ShardingModelCustomizer>();             }   } 

DbContextBase构造函数修改

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

  • 构造函数传参
  • 通过接口获取

构造函数传参

 public string ShardingRule { get; set; }  public DbContextBase(string shardingRule, DbContextOptions options) : base(options)  {             ShardingRule = shardingRule;  } 

通过接口获取

IShardingRule是实现规则名称的自定义接口,自行实现

 protected DbContextBase(DbContextOptions options, IServiceProvider serviceProvider)           : base(options)  {             ShardingRule = (serviceProvider.GetService<IShardingRule>()).GetValue();  } 

使用方式

这里只介绍构造函数传参使用方式

 DbContextOptionsBuilder<DbContextBase> optionsBuilder = new DbContextOptionsBuilder<DbContextBase>();  optionsBuilder.UseSqlServer("connStr");  var options =  optionsBuilder.Options;  using (var dbContext = new DbContextBase("202209", options))  {         //TODO....                } 

跨上下文使用事务

这里需要注意的是,跨上下文使用事务必须使用同一个连接,所以optionsBuilder.UseSqlServer(connection);这里的写法改变一下,使用同一连接

            DbContextOptionsBuilder<DbContextBase> optionsBuilder = new DbContextOptionsBuilder<DbContextBase>();             IDbConnection connection = new SqlConnection("connStr");             optionsBuilder.UseSqlServer(connection);             var options =  optionsBuilder.Options;             using (var dbContext = new DbContextBase("202209", options))             {                 using (var  transaction =await dbContext.Database.BeginTransactionAsync())                 {                     using (var dbContext2 = new DbContextBase("202210", options))                     {                          await dbContext2.Database.UseTransactionAsync(transaction);                         //TODO....                         transaction.Commit();                     }                 }                             } 

总结

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

发表评论

相关文章