实现原理
当我们new一个上下文DbContext 后, 每次执行CURD方式时 ,都会依次调用OnConfiguring(),OnModelCreating()两个方法。
- OnConfiguring() 我们将用来替换一些服务实现,以支持分表的工作
- OnModelCreating() 我们将用来重新实现 实体与数据库表 的映射关系
每次调用OnModelCreating()时,会判断实体与数据库表的映射关系有没有改变,如果改变则采用新的映射关系。
判断是否发生改变,通过替换 IModelCacheKeyFactory 接口的实现来完成。详情可见:在具有相同 DbContext 类型的多个模型之间进行交替
IModelCacheKeyFactory 实现
DbContextBase 是一个DbContext的实现,,ShardingRule是DbContextBase的一个共有属性。
根据分表规则的不同,每次的映射关系也会不同。- 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() 时,方法内部会调用实现IModelCustomizer的 ModelCustomizer.cs的Customize()方法,我们可以将映射关系写在此方法内。
通过继承实现:
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分表的实现大致全是这样,没有什么区别。可以参考一些开源的框架,对现有的系统进行适当的调整,毕竟别人写的并不一定适合你。希望这篇文章可以帮到你。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |