ShardingCore易用、简单、高性能、普适性,是一款扩展针对efcore生态下的分表分库的扩展解决方案,支持efcore2的所有版本,支持efcore2的所有数据库、支持自定义路由、动态路由、高性能分页、读写分离的一款组件,如果你喜欢这组件或者这个组件对你有帮助请点击下发star让更多的。neter可以看到使用 GiteeStar助力dotnet生态GithubStar背景 你是否在使用efcore,你是否在使用abp,你是否对目前的分表十分厌恶,手动指定表让你的代码无辜多出很多胶水代码,那么这次的文章可以很好的帮你解决掉当前的困难点,shardingcore针对efcore的分库分表读写分离的痛点进行扩展,可以完美集成到efcore生态的系统里面,无论你是abp还是其他使用efcore的框架,你确定真的不看一下吗,如何集成abp当时我发布这个库的时候就有很多人问我是否支持,我的回答是支持的,只是个人没有时间去实现。这次已经实现了我这边将分享下如何集成shardingcore到abpvnext中。 距离上一篇博客已经两周了,在这两周期间本人还是做了很多事情,针对优化shardingcore的使用和体验,就在上周五傍晚的时候有个使用abp的同学联系我,问我什么时候支持abpvnext,其实这个计划我很早之前就在issue里面备注了,只是获取用abp的同学没有怎么关注这个类库也没人提出来,所以就搁置了。因为shardingcore是一款几乎可以说可以集成到任何efcore生态下的所以原则上abp上集成应该是没什么难度的,因为本人使用abp不是很多所以这边自己按官方教程进行了初步的项目搭建,然后又用了一会功夫了解了abp源码(之前有了解过)清楚了dbcontext的创建过程所以很快就继承好了一个todoapp 接下来我将用一篇博客的篇幅来介绍如何将sharingcore集成到abpvnext中。如何集成abp项目创建下载 我这边是通过github进行了例子的todoapp进行下载,下载后是一个集合例子,我们获取TodoApp项目进行单独处理,打开然后编译。 注意如果你自己会新建那么也是一样的集成abp思路用过abp的用户都应该知道abp的dbcontext没有什么不一样,唯一需要注意的就是abp:只要你的dbcontext继承至publicclassTDbContext:AbpDbContext那么就可以完美使用。 但是shardingcore的使用我们通过readme来查看发现sharidngcore:只要你的dbcontext继承至publicclassTDbContext:AbstractShardingDbContext那么你就可以完美使用。 好家伙直接给想自己集成的同学搞蒙蔽了,c又不是c艹没有多继承啊那怎么办,但是这边其实是有一个误区的就是abp确实需要继承abpdbcontext但是shardingcore是已接口作为依赖来开发的,所以我们只需要实现ISharingDbContext这个接口就可以了如果需要事务在实现ISupportShardingTransaction 最终我们是通过实现一个抽象基类来继承abpdbcntext并且实现shardingcore需要的接口AbstractShardingAbpDbContext这样我们就可以在不破坏abp的同时又兼顾了shardingcore 注意:这边sharingcore让你们继承AbstractShardingDbContext是因为重复写这些接口的实现会很麻烦所以给你们写了一个抽象方便你们使用 abp集成注意事项通过源码可以看出abp集成需要赋值lazyserviceprovider因为abp的dbcontext是交由uow自己处理并且需要支持很多特性所以我们在创建dbcontext的时候需要对此处进行赋值注意点。注意abp默认提供了IEntity,IHasCreationTime属性较为常用所以我们需要注意如何支持这两种,因为当你用id取模分表或者创建时间分表的使用场景还是比较常见的所以我们需要支持。 因为在insert时如果shardingcore发现对应的分表字段为null就无法继续执行下去,所以为了兼容abp需要支持两个比较常见的需求赋值依赖注入支持domainevent等事件 自动属性 了解sahrdingcore针对dbcontext的分表支持 首先我们需要知道shardingcore是如何对一个普通的dbcontext进行支持的 如果你的dbcontext有用到以下任意一个接口那么集成起来可能需要自己去实现对应的接口IDbSetSource用来接管dbsetIQueryCompiler用来接管查询编译IDbContextTransactionManager用来接管事务开启IRelationalTransactionFactory用来接管事务的提交、回滚和加入IModelCacheKeyFactory用来接管dbcontext的模型缓存IModelCustomizer用来接管dbcontext的模型初始化前后自定义 如果你的efcore想接入shardingcore并且如果你没有对dbcontext的上述任何接口进行替换那么可以很容易就接入,如果你的efcore在创建的时候有针对上述的接口进行替换,就需要你自己手动进行两边的实现合并。如何接入shardingcore 这边我们假设你没有对上述的dbcontextoptionbuilder的创建进行接口的替换那么你只需要进行如下操作就可以简单接入shardingcore首先就是默认创建dbcontext替换为shardingcore的配置 原先:publicvoidConfigureServices(IServiceCollectionservices)services。AddDbContextDefaultShardingTableDbContext(oo。UseSqlServer(Server。;DatabaseTodoApp;TrustedConnectionTrue)); 现在:publicvoidConfigureServices(IServiceCollectionservices)1。先检查dbcontext构造函数只允许DbContextOptionsTodoAppDbContextoptionsShardingCoreHelper。CheckContextConstructorsDefaultShardingTableDbContext();2。添加UseShardingDefaultShardingTableDbContext()让依赖注入创建的dbcontext支持查询插入事务的管理services。AddDbContextDefaultShardingTableDbContext(oo。UseSqlServer(Server。;DatabaseTodoApp;TrustedConnectionTrue)。UseShardingDefaultShardingTableDbContext());3。添加分表配置newShardingCoreConfigBuilderDefaultShardingTableDbContext(context。Services,((s,builder){builder。UseSqlServer(s);}))。Begin(o{o。CreateShardingTableOnStartfalse;o。EnsureCreatedWithOutShardingTablefalse;o。AutoTrackEntitytrue;})。AddShardingTransaction((connection,builder)builder。UseSqlServer(connection))。AddDefaultDataSource(ds0,Server。;DatabaseTodoApp;TrustedConnectionTrue)。AddShardingTableRoute(o{o。AddShardingTableRouteToDoItemVirtualTableRoute();})。End();publicvoidConfigure(IApplicationBuilderapp,IWebHostEnvironmentenv)4。初始化分表配置varshardingBootstrapperapp。ApplicationServices。GetRequiredServiceIShardingBootstrapper();shardingBootstrapper。Start(); 综上所述我们接入任何efcore的系统只需要进行4步(第一步都可以去掉只需要3步)就可以完美接入了,可以保证使用abp正式接入替换 修改将todoitem表作为id取模来进行分表演示修改TodoItem实体 默认TodoApp有一个TodoItem实体对象我们首先创建两个空接口标识对应分表对像的i分表字段是id,是自动创建的guidpublicinterfaceIShardingKeyIsGuId{}标识对应的分表对应的分表字段是创建时间publicinterfaceIShardingKeyIsCreationTime{}修改TodoItempublicclassTodoItem:BasicAggregateRootGuid,IShardingTable,IShardingKeyIsGuId{〔ShardingTableKey〕publicoverrideGuidId{get;protectedset;}publicstringText{get;set;}} 创建TodoItem的分表路由 通过继承默认分表取模路由AbstractSimpleShardingModKeyStringVirtualTableRouteid取模虽然是string但是guid也是一样的最多两位就是0099,按5来取模publicclassToDoItemVirtualTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRouteTodoItem{publicToDoItemVirtualTableRoute():base(2,5){}}实现抽象类修改TodoItemDbContext 实现AbstractShardingAbpDbContextpublicclassTodoAppDbContext:AbstractShardingAbpDbContextTodoAppDbContext,IIdentityDbContext,ITenantManagementDbContext,IShardingTableDbContext修改实体TodoItempublicclassTodoItem:BasicAggregateRootGuid,IShardingTable,IShardingKeyIsGuId{〔ShardingTableKey〕publicoverrideGuidId{get;protectedset;}publicstringText{get;set;}} 其中别的接口都和shardingcore一致,为了支持abp的部分自动属性这边进行了新的接口添加用来标识当前的对象是通过什么方式来进行分表的,然后可以高效的通过接口来进行赋值,比如IShardingKeyIsGuId告诉系统是id为guid的进行分表的publicabstractclassAbstractShardingAbpDbContextTDbContext。。。{。。。。。。privatevoidCheckAndSetShardingKeyThatSupportAutoCreateTEntity(TEntityentity)whereTEntity:class{if(entityisIShardingKeyIsGuId){if(entityisIEntityGuidguidEntity){if(guidEntity。Id!default){return;}varidPropertyentity。GetProperty(nameof(IEntityGuid。Id));vardbGeneratedAttrReflectionHelper。GetSingleAttributeOrDefaultDatabaseGeneratedAttribute(idProperty);if(dbGeneratedAttr!nulldbGeneratedAttr。DatabaseGeneratedOption!DatabaseGeneratedOption。None){return;}EntityHelper。TrySetId(guidEntity,()GuidGenerator。Create(),true);}}elseif(entityisIShardingKeyIsCreationTime){AuditPropertySetter?。SetCreationProperties(entity);}}}添加虚拟路由 既然你讲TodoItem进行了分表,那么你这边需要告诉系统你是按怎么个规则进行分表的,假设我们默认按id取模那么可以继承shardingcore默认提供的取模路由publicclassToDoItemVirtualTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRouteTodoItem{publicToDoItemVirtualTableRoute():base(2,5){}} 简单说明就是分表后缀为2位数0099,5代表模5也就是00,01,02,03,04 注意:IShardingTableDbContext如果dbcontext需要实现分表功能必须实现IShardingTableDbContext 到目前为止我们的准备工作已经完成了,接下来需要进行codefirst的支持和具体项目的配置使用了 选中TodoApp。EntityFrameworkCore项目打开TodoAppDbContextFactory替换dbcontext的创建方法,主要是替换codefirst的建表语句 这边是采用了EFCore。ShardingstaticTodoAppDbContextFactory(){varservicesnewServiceCollection();varconfigurationBuildConfiguration();services。AddShardingDbContextTodoAppDbContext((conn,o)o。UseSqlServer(conn)。ReplaceServiceIMigrationsSqlGenerator,ShardingSqlServerMigrationsSqlGeneratorTodoAppDbContext())。Begin(o{o。AutoTrackEntitytrue;})。AddShardingTransaction((connection,builder)builder。UseSqlServer(connection))。AddDefaultDataSource(ds0,configuration。GetConnectionString(Default))。AddShardingTableRoute(o{o。AddShardingTableRouteToDoItemVirtualTableRoute();})。End();services。AddLogging();varbuildServiceProviderservices。BuildServiceProvider();ShardingContainer。SetServices(buildServiceProvider);newShardingBootstrapper(buildServiceProvider)。Start();}publicTodoAppDbContextCreateDbContext(string〔〕args){returnShardingContainer。GetServiceTodoAppDbContext();} 主要代码就是告诉efcore。tools如何创建对应的dbcontext 然后打开nuget控制台 选中需要生成迁移的项目 启动项设置为 执行命令PMAddMigrationInitTodoApp PMupdatedatabase 到此为止我们的codefirst已经完成了,系统会自动根据分表的配置来进行创建对应的sql语句abp启动 因为shardingcore是基于接口和dbcontext所以只要你的efcore那么基本上你的生态就可以接入shardingcore,主要就是注意1点自定义替换DbContextOptions的部分服务dbcontext的构造函数是DbContextOptions或者是他的泛型类修改TodoAppEntityFrameworkCoreModulepublicoverridevoidConfigureServices(ServiceConfigurationContextcontext){。。。。。。Configure(options{ThemainpointtochangeyourDBMS。SeealsoTodoAppDbContextFactoryforEFCoretooling。options。UseSqlServer();options。ConfigureTodoAppDbContext(context1{context1。DbContextOptions。UseSqlServer(Server。;DatabaseTodoApp;TrustedConnectionTrue)。UseShardingTodoAppDbContext();});});ShardingCoreHelper。CheckContextConstructorsTodoAppDbContext();newShardingCoreConfigBuilderTodoAppDbContext(context。Services,((s,builder){builder。UseSqlServer(s);}))。Begin(o{o。CreateShardingTableOnStartfalse;o。EnsureCreatedWithOutShardingTablefalse;o。AutoTrackEntitytrue;})。AddShardingTransaction((connection,builder)builder。UseSqlServer(connection))。AddDefaultDataSource(ds0,Server。;DatabaseTodoApp;TrustedConnectionTrue)。AddShardingTableRoute(o{o。AddShardingTableRouteToDoItemVirtualTableRoute();})。End();}publicoverridevoidOnPostApplicationInitialization(ApplicationInitializationContextcontext){context。ServiceProvider。GetRequiredServiceIShardingBootstrapper()。Start();} 稍微解释下options。ConfigureTodoAppDbContext(context1{context1。DbContextOptions。UseSqlServer(Server。;DatabaseTodoApp;TrustedConnectionTrue)。UseShardingTodoAppDbContext();}); 用来告诉abp,TodoAppDbContext的创建需要使用useSharding, 之后就是shardingcore默认提供的builder,当然你们可以自行封装一下,别忘了在启动的时候context。ServiceProvider。GetRequiredServiceIShardingBootstrapper()。Start(); 这个千万不能忘运行TodoApp。Web 通过添加efcore的日志我们可以清晰地看到abp能够正确的将对应的数据插入进去,并且完全不需要修改现有代码,基本上的零基础使用,简单的配置, 如果您喜欢本库就点点star点点赞,来都来了点个推荐不过分吧。为。net生态做一份贡献,希望各位个多多提issue,和pr十分感激