Abp VNext 分表分库

ShardingCore

ShardingCore 易用、简单、高性能、普适性,是一款扩展针对efcore生态下的分表分库的扩展解决方案,支持efcore2+的所有版本,支持efcore2+的所有数据库、支持自定义路由、动态路由、高性能分页、读写分离的一款组件,如果你喜欢这组件或者这个组件对你有帮助请点击下发star让更多的.neter可以看到使用


Gitee Star 助力dotnet 生态 Github Star


背景

你是否在使用efcore,你是否在使用abp,你是否对目前的分表十分厌恶,手动指定表让你的代码无辜多出很多胶水代码,那么这次的文章可以很好的帮你解决掉当前的困难点,sharding-core 针对efcore的分库分表读写分离的痛点进行扩展,可以完美集成到efcore生态的系统里面,无论你是abp还是其他使用efcore的框架,你确定真的不看一下吗,如何集成abp当时我发布这个库的时候就有很多人问我是否支持,我的回答是支持的,只是个人没有时间去实现。这次已经实现了我这边将分享下如何集成sharding-coreabp vnext中。

距离上一篇博客已经两周了,在这两周期间本人还是做了很多事情,针对优化sharding-core的使用和体验,就在上周五傍晚的时候有个使用abp的同学联系我,问我什么时候支持abp vnext,其实这个计划我很早之前就在issue里面备注了,只是获取用abp的同学没有怎么关注这个类库也没人提出来,所以就搁置了。因为sharding-core是一款几乎可以说可以集成到任何efcore生态下的所以原则上abp上集成应该是没什么难度的,因为本人使用abp不是很多所以这边自己按官方教程进行了初步的项目搭建,然后又用了一会功夫了解了abp源码(之前有了解过)清楚了dbcontext的创建过程所以很快就继承好了一个todoapp

接下来我将用一篇博客的篇幅来介绍如何将sharing-core集成到abp vnext中。

如何集成

abp项目创建/下载

我这边是通过github进行了例子的todoapp 进行下载,下载后是一个集合例子,我们获取TodoApp项目进行单独处理,打开然后编译。

注意如果你自己会新建那么也是一样的

集成abp思路

用过abp的用户都应该知道abp的dbcontext没有什么不一样,唯一需要注意的就是
  • abp:只要你的dbcontext继承至 public class TDbContext:AbpDbContext<TDbContext>那么就可以完美使用.

    但是sharding-core的使用我们通过readme来查看发现
  • sharidng-core:只要你的dbcontext继承至public class TDbContext:AbstractShardingDbContext那么你就可以完美使用.

好家伙直接给想自己集成的同学搞蒙蔽了,c#又不是c艹没有多继承啊那怎么办,但是这边其实是有一个误区的就是abp确实需要继承abpdbcontext但是sharding-core是已接口作为依赖来开发的,所以我们只需要实现ISharingDbContext 这个接口就可以了如果需要事务在实现ISupportShardingTransaction

最终我们是通过实现一个抽象基类来继承abpdbcntext并且实现sharding-core需要的接口 AbstractShardingAbpDbContext 这样我们就可以在不破坏abp的同时又兼顾了sharding-core

注意:这边sharing-core让你们继承AbstractShardingDbContext是因为重复写这些接口的实现会很麻烦所以给你们写了一个抽象方便你们使用

abp集成注意事项

  • 通过源码可以看出abp集成需要赋值lazyserviceprovider 因为abp的dbcontext是交由uow自己处理并且需要支持很多特性所以我们在创建dbcontext的时候需要对此处进行赋值注意点。
  • 注意abp默认提供了IEntity<Guid>,IHasCreationTime属性较为常用所以我们需要注意如何支持这两种,因为当你用id取模分表或者创建时间分表的使用场景还是比较常见的所以我们需要支持。

    因为在insert时如果sharding-core发现对应的分表字段为null就无法继续执行下去,所以为了兼容abp需要支持两个比较常见的需求

赋值依赖注入支持domain event等事件

自动属性

了解sahrding-core针对dbcontext的分表支持

首先我们需要知道sharding-core是如何对一个普通的dbcontext进行支持的

如果你的dbcontext有用到以下任意一个接口那么集成起来可能需要自己去实现对应的接口

  • IDbSetSource 用来接管dbset
  • IQueryCompiler 用来接管查询编译
  • IDbContextTransactionManager 用来接管事务开启
  • IRelationalTransactionFactory 用来接管事务的提交、回滚 和加入
  • IModelCacheKeyFactory 用来接管dbcontext的模型缓存
  • IModelCustomizer 用来接管dbcontext的模型初始化前后自定义

如果你的efcore想接入sharding-core并且如果你没有对dbcontext的上述任何接口进行替换那么可以很容易就接入,如果你的efcore在创建的时候有针对上述的接口进行替换,就需要你自己手动进行两边的实现合并。

如何接入sharding-core

这边我们假设你没有对上述的dbcontextoptionbuilder的创建进行接口的替换那么你只需要进行如下操作就可以简单接入sharding-core

  • 首先就是默认创建dbcontext替换为sharding-core的配置

原先:

public void ConfigureServices(IServiceCollection services)

            services.AddDbContext<DefaultShardingTableDbContext>(o => o.UseSqlServer("Server=.;Database=TodoApp;Trusted_Connection=True"));

现在:


public void ConfigureServices(IServiceCollection services) //1.先检查dbcontext构造函数只允许 DbContextOptions<TodoAppDbContext> options
ShardingCoreHelper.CheckContextConstructors<DefaultShardingTableDbContext>();
//2.添加UseSharding<DefaultShardingTableDbContext>()让依赖注入创建的dbcontext支持查询插入事务的管理
services.AddDbContext<DefaultShardingTableDbContext>(o=>o.UseSqlServer("Server=.;Database=TodoApp;Trusted_Connection=True").UseSharding<DefaultShardingTableDbContext>());
//3.添加分表配置
new ShardingCoreConfigBuilder<DefaultShardingTableDbContext>(context.Services,((s, builder) =>
{
builder.UseSqlServer(s);
} )).Begin(o =>
{
o.CreateShardingTableOnStart = false;
o.EnsureCreatedWithOutShardingTable = false;
o.AutoTrackEntity = true;
})
.AddShardingTransaction((connection, builder) =>
builder.UseSqlServer(connection))
.AddDefaultDataSource("ds0", "Server=.;Database=TodoApp;Trusted_Connection=True")
.AddShardingTableRoute(o =>
{
o.AddShardingTableRoute<ToDoItemVirtualTableRoute>();
}).End(); public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
//4.初始化分表配置
var shardingBootstrapper = app.ApplicationServices.GetRequiredService<IShardingBootstrapper>();
shardingBootstrapper.Start();

综上所述我们接入任何efcore的系统只需要进行4步(第一步都可以去掉只需要3步)就可以完美接入了,可以保证使用

abp正式接入替换

修改将todoitem表作为id取模来进行分表演示

修改TodoItem实体

默认TodoApp有一个TodoItem实体对象我们首先创建两个空接口

//标识对应分表对像的i分表字段是id,是自动创建的guid
public interface IShardingKeyIsGuId
{
}
//标识对应的分表对应的分表字段是创建时间
public interface IShardingKeyIsCreationTime
{
}
//修改TodoItem
public class TodoItem : BasicAggregateRoot<Guid>,IShardingTable,IShardingKeyIsGuId
{
[ShardingTableKey]
public override Guid Id { get; protected set; }
public string Text { get; set; }
}

创建TodoItem的分表路由

通过继承默认分表取模路由AbstractSimpleShardingModKeyStringVirtualTableRoute

//id取模虽然是string但是guid也是一样的最多两位就是00_99,按5来取模
public class ToDoItemVirtualTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<TodoItem>
{
public ToDoItemVirtualTableRoute() : base(2, 5)
{
}
}

实现抽象类修改TodoItemDbContext

实现 AbstractShardingAbpDbContext


public class TodoAppDbContext :AbstractShardingAbpDbContext<TodoAppDbContext>,
IIdentityDbContext,
ITenantManagementDbContext,
IShardingTableDbContext

修改实体TodoItem

 public class TodoItem : BasicAggregateRoot<Guid>,IShardingTable,IShardingKeyIsGuId
{
[ShardingTableKey]
public override Guid Id { get; protected set; }
public string Text { get; set; }
}

其中别的接口都和sharding-core一致,为了支持abp的部分自动属性这边进行了新的接口添加用来标识当前的对象是通过什么方式来进行分表的,然后可以高效的通过接口来进行赋值,比如IShardingKeyIsGuId告诉系统是id为guid的进行分表的

public abstract class AbstractShardingAbpDbContext<TDbContext>...
{
......
private void CheckAndSetShardingKeyThatSupportAutoCreate<TEntity>(TEntity entity) where TEntity : class
{
if (entity is IShardingKeyIsGuId)
{ if (entity is IEntity<Guid> guidEntity)
{
if (guidEntity.Id != default)
{
return;
}
var idProperty = entity.GetProperty(nameof(IEntity<Guid>.Id)); var dbGeneratedAttr = ReflectionHelper
.GetSingleAttributeOrDefault<DatabaseGeneratedAttribute>(
idProperty
); if (dbGeneratedAttr != null && dbGeneratedAttr.DatabaseGeneratedOption != DatabaseGeneratedOption.None)
{
return;
} EntityHelper.TrySetId(
guidEntity,
() => GuidGenerator.Create(),
true
);
}
}
else if (entity is IShardingKeyIsCreationTime)
{
AuditPropertySetter?.SetCreationProperties(entity);
}
}
}

添加虚拟路由

既然你讲TodoItem进行了分表,那么你这边需要告诉系统你是按怎么个规则进行分表的,假设我们默认按id取模那么可以继承sharding-core默认提供的取模路由


public class ToDoItemVirtualTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<TodoItem>
{
public ToDoItemVirtualTableRoute() : base(2, 5)
{
}
}

简单说明就是分表后缀为2位数00-99,5代表模5也就是00,01,02,03,04

注意: IShardingTableDbContext如果dbcontext需要实现分表功能必须实现IShardingTableDbContext

到目前为止我们的准备工作已经完成了,接下来需要进行codefirst的支持和具体项目的配置使用了

选中TodoApp.EntityFrameworkCore项目打开TodoAppDbContextFactory替换dbcontext的创建方法,主要是替换codefirst的建表语句

这边是采用了EFCore.Sharding

 static TodoAppDbContextFactory()
{
var services = new ServiceCollection();
var configuration = BuildConfiguration();
services.AddShardingDbContext<TodoAppDbContext>(
(conn, o) =>
o.UseSqlServer(conn)
.ReplaceService<IMigrationsSqlGenerator, ShardingSqlServerMigrationsSqlGenerator<TodoAppDbContext>>()
).Begin(o =>
{
o.AutoTrackEntity = true;
})
.AddShardingTransaction((connection, builder) =>
builder.UseSqlServer(connection))
.AddDefaultDataSource("ds0",
configuration.GetConnectionString("Default"))
.AddShardingTableRoute(o =>
{
o.AddShardingTableRoute<ToDoItemVirtualTableRoute>();
}).End();
services.AddLogging();
var buildServiceProvider = services.BuildServiceProvider();
ShardingContainer.SetServices(buildServiceProvider);
new ShardingBootstrapper(buildServiceProvider).Start();
} public TodoAppDbContext CreateDbContext(string[] args)
{
return ShardingContainer.GetService<TodoAppDbContext>();
}

主要代码就是告诉efcore.tools如何创建对应的dbcontext

然后打开nuget控制台



选中需要生成迁移的项目



启动项设置为

执行命令

PM> Add-Migration InitTodoApp

PM> update-database

到此为止我们的code first已经完成了,系统会自动根据分表的配置来进行创建对应的sql语句

abp启动

因为sharding-core是基于接口和dbcontext所以只要你的efcore那么基本上你的生态就可以接入sharding-core,主要就是注意1点

  • 自定义替换DbContextOptions的部分服务
  • dbcontext的构造函数是DbContextOptions或者是他的泛型类

修改TodoAppEntityFrameworkCoreModule

public override void ConfigureServices(ServiceConfigurationContext context)
{
......
Configure<AbpDbContextOptions>(options =>
{
/* The main point to change your DBMS.
* See also TodoAppDbContextFactory for EF Core tooling. */
options.UseSqlServer();
options.Configure<TodoAppDbContext>(context1 =>
{
context1.DbContextOptions.UseSqlServer("Server=.;Database=TodoApp;Trusted_Connection=True").UseSharding<TodoAppDbContext>();
});
});
ShardingCoreHelper.CheckContextConstructors<TodoAppDbContext>();
new ShardingCoreConfigBuilder<TodoAppDbContext>(context.Services,((s, builder) =>
{
builder.UseSqlServer(s);
} )).Begin(o =>
{
o.CreateShardingTableOnStart = false;
o.EnsureCreatedWithOutShardingTable = false;
o.AutoTrackEntity = true;
})
.AddShardingTransaction((connection, builder) =>
builder.UseSqlServer(connection))
.AddDefaultDataSource("ds0", "Server=.;Database=TodoApp;Trusted_Connection=True")
.AddShardingTableRoute(o =>
{
o.AddShardingTableRoute<ToDoItemVirtualTableRoute>();
}).End();
} public override void OnPostApplicationInitialization(ApplicationInitializationContext context)
{
context.ServiceProvider.GetRequiredService<IShardingBootstrapper>().Start();
}

稍微解释下

   options.Configure<TodoAppDbContext>(context1 =>
{
context1.DbContextOptions.UseSqlServer("Server=.;Database=TodoApp;Trusted_Connection=True").UseSharding<TodoAppDbContext>();
});

用来告诉abp,TodoAppDbContext的创建需要使用useSharding,

之后就是sharding-core默认提供的builder,当然你们可以自行封装一下,别忘了在启动的时候

            context.ServiceProvider.GetRequiredService<IShardingBootstrapper>().Start();

这个千万不能忘

运行TodoApp.Web



通过添加efcore的日志我们可以清晰地看到abp能够正确的将对应的数据插入进去,并且完全不需要修改现有代码,基本上的零基础使用,简单的配置,

如果您喜欢本库就点点star点点赞,来都来了点个推荐不过分吧。为.net生态做一份贡献,希望各位个多多提issue,和pr十分感激

项目demo

AbpVNextShardingTodoApp

分表分库组件求赞求star


博客

QQ群:771630778

个人QQ:326308290(欢迎技术支持提供您宝贵的意见)

个人邮箱:326308290@qq.com

特别感谢

Abp VNext分表分库,拒绝手动,我们要happy coding的更多相关文章

  1. .NETCore 下支持分表分库、读写分离的通用 Repository

    首先声明这篇文章不是标题党,我说的这个类库是 FreeSql.Repository,它作为扩展库现实了通用仓储层功能,接口规范参考 abp vnext 定义,实现了基础的仓储层(CURD). 安装 d ...

  2. .Net下极限生产力之efcore分表分库全自动化迁移CodeFirst

    .Net下极限生产力之分表分库全自动化Migrations Code-First ## 介绍 本文ShardinfCore版本x.6.x.x+ 本期主角: - [`ShardingCore`](htt ...

  3. 总结下Mysql分表分库的策略及应用

    上月前面试某公司,对于mysql分表的思路,当时简要的说了下hash算法分表,以及discuz分表的思路,但是对于新增数据自增id存放的设计思想回答的不是很好(笔试+面试整个过程算是OK过了,因与个人 ...

  4. 重新学习Mysql数据13:Mysql主从复制,读写分离,分表分库策略与实践

    一.MySQL扩展具体的实现方式 随着业务规模的不断扩大,需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量. 关于数据库的扩展主要包括:业务拆分.主从复制.读写分离.数据库分库 ...

  5. efcore分表分库原理解析

    ShardingCore ShardingCore 易用.简单.高性能.普适性,是一款扩展针对efcore生态下的分表分库的扩展解决方案,支持efcore2+的所有版本,支持efcore2+的所有数据 ...

  6. Furion分表分库我也要happy coding

    Furion分表分库集成ShardingCore ShardingCore ShardingCore 易用.简单.高性能.普适性,是一款扩展针对efcore生态下的分表分库的扩展解决方案,支持efco ...

  7. .Net 下高性能分表分库组件-连接模式原理

    ShardingCore ShardingCore 一款ef-core下高性能.轻量级针对分表分库读写分离的解决方案,具有零依赖.零学习成本.零业务代码入侵. Github Source Code 助 ...

  8. .Net下你不得不看的分表分库解决方案-多字段分片

    .Net下你不得不看的分表分库解决方案-多字段分片 介绍 本期主角:ShardingCore 一款ef-core下高性能.轻量级针对分表分库读写分离的解决方案,具有零依赖.零学习成本.零业务代码入侵 ...

  9. NetCore框架WTM的分表分库实现

    介绍 本期主角: ShardingCore 一款ef-core下高性能.轻量级针对分表分库读写分离的解决方案,具有零依赖.零学习成本.零业务代码入侵 WTM WalkingTec.Mvvm框架(简称W ...

随机推荐

  1. MySQL实战45讲(21--25)-笔记

    21 | 为什么我只改一行的语句,锁这么多? 加锁规则里面:包含了两个"原则".两个"优化"和一个"bug". 原则 1:加锁的基本单位是 ...

  2. json包中的Marshal&Unmarshal 文档译本

    Marshal func Marshal(v interface{})([]byte, error) Marshal returns the JSON encoding of v. Marshal返回 ...

  3. plsql报ora-00911错误的解决

    论不作死就不会死的过程,楼主之前因为得了一个oralce9i的精简版的客户端安装包,我也分享过给其他人使用这个安装包,没听人反馈过说有问题,所以这次换了电脑后果断就安装这个客户端.然后问题就来了. 反 ...

  4. CodeForce-702C Cellular Network(查找)

    Cellular Network CodeForces - 702C 给定 n (城市数量) 和 m (灯塔数量): 给定 a1~an 城市坐标: 给定 b1~bm 灯塔坐标: 求出灯塔照亮的最小半径 ...

  5. 第一次用AngularJS

    1.创建指令的4种方式(ECMA) var appModule = angular.module('app', []); appModule.directive('hello', function() ...

  6. scrum项目冲刺_day09总结

    摘要:今日完成任务. 1.短信服务完成(由于使用免费的接口,导致部分手机会收到垃圾短信) 2.注册登录完成 3.导航还在进行 总任务: 一.appUI页面(已完成) 二.首页功能: 1.图像识别功能( ...

  7. 学习PHP中Fileinfo扩展的使用

    今天来学习的这个扩展其实现在也已经是标配的一个扩展了,为什么呢?因为 Laravel 框架在安装的时候它就是必须的一个扩展,没有打开它的话,连 Laravel 框架都是无法使用的. Fileinfo ...

  8. TP5模型开启事务

    和Db开启事务类似,Db是静态方法 $userObj = new UserModel(); $userObj->startTrans(); try { $userObj->data($da ...

  9. Jmeter扩展组件开发(10) - 自定义扩展函数助手的开发

    CODE package com.functions;import org.apache.jmeter.engine.util.CompoundVariable;import org.apache.j ...

  10. windows日志查看与清理

    日志查看 (1) 启动Windows实验台,点击:开始 - 控制面板 - 管理工具 - 事件查看器. (2) 应用程序日志.安全日志.系统日志.DNS日志默认位置:%sys temroot%\syst ...