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(); }使用方式【EFCore分表實現】這里只介紹構造函數傳參使用方式
DbContextOptionsBuilder<DbContextBase> optionsBuilder = new DbContextOptionsBuilder<DbContextBase>(); optionsBuilder.UseSqlServer("connStr"); var options =optionsBuilder.Options; using (var dbContext = new DbContextBase("202209", options)) {//TODO.... }

推薦閱讀