• 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();
    }
    }

    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();
    //查找需要重新映射表名的类
    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().ReplaceService();
    }
    }

    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()).GetValue();
    }

    使用方式

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

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

    跨上下文使用事务

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

    DbContextOptionsBuilder optionsBuilder = new DbContextOptionsBuilder();
    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分表的实现大致全是这样,没有什么区别。可以参考一些开源的框架,对现有的系统进行适当的调整,毕竟别人写的并不一定适合你。希望这篇文章可以帮到你。

  • 相关阅读:
    Java实现更新excel文件内容
    细胞膜修饰两亲性接枝聚合物/荧光探针/荧光染料/水凝胶/仿生纳米颗粒(ICNPs)的研究与制备
    软件测试面试必问:为什么要选择软件测试?
    k8s安装3节点集群Fate v1.8.0
    艾美捷Enzo人 IgG1 同种型对照化学参数及文献参考
    安卓文件权限总结
    .NET6: 开发基于WPF的摩登三维工业软件 (2)
    使用分页查询(mybatis自带的)selectPage(page,wrapper)时报错
    【2022新生学习】第四周要点
    Java 集合框架,泛型,包装类
  • 原文地址:https://www.cnblogs.com/queque/p/16730570.html