做公司网站的模板,湛江seo排名外包,为什么百度搜索不到我的网站,网站是用什么软件做的开始本次我们的主题就是极限生产力,其他语言望尘莫及的分表分库全自动化Migrations Code-First 加 efcore 分表分库无感开发还记得上次发布博客还是在上次,上次发布了如何兼容WTM框架后也有不少小伙伴来问我如何兼容如何迁移等问题,经过这么多框架的兼容我自己也认识到了一些问… 开始本次我们的主题就是极限生产力,其他语言望尘莫及的分表分库全自动化Migrations Code-First 加 efcore 分表分库无感开发还记得上次发布博客还是在上次,上次发布了如何兼容WTM框架后也有不少小伙伴来问我如何兼容如何迁移等问题,经过这么多框架的兼容我自己也认识到了一些问题,譬如在ShardingCore初始化前使用(毕竟efcore)的初始化是在依赖注入的时候不需要手动调用初始化比如efcore.tool的迁移的问题,本项目不能迁移,因为efcore.tool在使用命令的时候不会调用Configure导致无法初始化的bug,导致迁移必须要通过新建控制台程序,而不能在本项目内迁移,再或者code-first和ShardingCore的启动参数冲突导致需要平凡修改并且不支持分库,之前有小伙伴分了300个库如果自动迁移不能用确实是一件很头疼的事情,虽然这些问题对于分库分表而言其实是小事情但是如果一旦分表分库到达一定的量级就会难以维护。所以ShardingCore在最近三周内开启了新的版本,新版本主要是解决上述痛点并且将代码更加标准的使用开发软件一般是先能用然后好用最后标准化ShardingCore也是如此因为需要扩展efcore所以有时候在不熟悉efcore的扩展方式的时候只能靠静态类来进行注入访问,而静态类其实是一个非常不标准的用法,除非万不得已。那么新版本x.6.x.x ShardingCore带来了什么请往下看移除静态容器静态容器的使用导致ShardingCore在整个应用程序声明周期只有一份数据,那么数据都是共享的这个对于后续的测试维护扩展是相当的不利的没有单例那种隔离性来的好,所以移除了ShardingContainer,通过提供IShardingRuntimeContext来保证和之前的参数结构的访问,同一个DbContext类型在使用不同的IShardingRuntimeContext后可以表现出不同的分表分库特性。原生efcore首先我们针对原生efcore进行扩展来达到分库分表code-first自动迁移开发添加依赖 ShardingCore 6.6.0.3 MySql//请安装最新版本目前x.6.0.3,第一个版本号6代表efcore的版本号
Install-Package ShardingCore -Version 6.6.0.3Install-Package Pomelo.EntityFrameworkCore.MySql -Version 6.0.1
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.6创建一个todo实体public class TodoItem{public string Id { get; set; }public string Text { get; set; }
}创建dbcontext简单的将对象和数据库做了一下映射当然DbSetAttribute也是可以的public class MyDbContext:AbstractShardingDbContext,IShardingTableDbContext
{public MyDbContext(DbContextOptionsMyDbContext options) : base(options){}public IRouteTail RouteTail { get; set; }protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.EntityTodoItem(mb {mb.HasKey(o o.Id);mb.Property(o o.Id).IsRequired().HasMaxLength(50).HasComment(id);mb.Property(o o.Text).IsRequired().HasMaxLength(256).HasComment(事情);mb.ToTable(nameof(TodoItem));});}
}新建分库分表路由分库路由public class TodoItemDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRouteTodoItem,string
{ /// summary/// id的hashcode取模余3分库/// /summary/// param nameshardingKey/param/// returns/returns/// exception crefInvalidOperationException/exceptionpublic override string ShardingKeyToDataSourceName(object shardingKey){ if (shardingKey null) throw new InvalidOperationException(sharding key cant null);var stringHashCode ShardingCoreHelper.GetStringHashCode(shardingKey.ToString()); return $ds{(Math.Abs(stringHashCode) % 3)};//ds0,ds1,ds2}private readonly Liststring _dataSources new Liststring() { ds0, ds1, ds2 };public override Liststring GetAllDataSourceNames(){ return _dataSources;}public override bool AddDataSourceName(string dataSourceName){throw new NotImplementedException();} /// summary/// id分库/// /summary/// param namebuilder/parampublic override void Configure(EntityMetadataDataSourceBuilderTodoItem builder){builder.ShardingProperty(o o.Id);}public override Funcstring, bool GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator){var t ShardingKeyToDataSourceName(shardingKey); switch (shardingOperator){ case ShardingOperatorEnum.Equal: return tail tail t; default:{ return tail true;}}}
}折叠分表路由public class TodoItemTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRouteTodoItem
{public TodoItemTableRoute() : base(2, 3){} /// summary/// 正常情况下不会用内容来做分片键因为作为分片键有个前提就是不会被修改/// /summary/// param namebuilder/parampublic override void Configure(EntityMetadataTableBuilderTodoItem builder){builder.ShardingProperty(o o.Text);}
}新建迁移数据库脚本生成public class ShardingMySqlMigrationsSqlGenerator:MySqlMigrationsSqlGenerator
{private readonly IShardingRuntimeContext _shardingRuntimeContext;public ShardingMySqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IRelationalAnnotationProvider annotationProvider, IMySqlOptions options,IShardingRuntimeContext shardingRuntimeContext) : base(dependencies, annotationProvider, options){_shardingRuntimeContext shardingRuntimeContext;}protected override void Generate(MigrationOperation operation,IModel model,MigrationCommandListBuilder builder){var oldCmds builder.GetCommandList().ToList();base.Generate(operation, model, builder);var newCmds builder.GetCommandList().ToList();var addCmds newCmds.Where(x !oldCmds.Contains(x)).ToList();MigrationHelper.Generate(_shardingRuntimeContext,operation, builder, Dependencies.SqlGenerationHelper, addCmds);}
}配置依赖注入ILoggerFactory efLogger LoggerFactory.Create(builder
{builder.AddFilter((category, level) category DbLoggerCategory.Database.Command.Name level LogLevel.Information).AddConsole();
});
var builder WebApplication.CreateBuilder(args);builder.Services.AddControllers();builder.Services.AddShardingDbContextMyDbContext().UseRouteConfig(op {op.AddShardingTableRouteTodoItemTableRoute();op.AddShardingDataSourceRouteTodoItemDataSourceRoute();}).UseConfig((sp,op) {op.UseShardingQuery((con, b) {b.UseMySql(con, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);});op.UseShardingTransaction((con, b) {b.UseMySql(con, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);});op.AddDefaultDataSource(ds0, server127.0.0.1;port3306;databasemydb0;useridroot;passwordroot;);op.AddExtraDataSource(spnew Dictionarystring, string(){{ds1, server127.0.0.1;port3306;databasemydb1;useridroot;passwordroot;},{ds2, server127.0.0.1;port3306;databasemydb2;useridroot;passwordroot;}});op.UseShardingMigrationConfigure(b {b.ReplaceServiceIMigrationsSqlGenerator, ShardingMySqlMigrationsSqlGenerator();});}).AddShardingCore();var app builder.Build();// Configure the HTTP request pipeline.//如果有按时间分片的需要加定时任务否则可以不加app.Services.UseAutoShardingCreate();using (var scope app.Services.CreateScope()){var defaultShardingDbContext scope.ServiceProvider.GetRequiredServiceMyDbContext(); if (defaultShardingDbContext.Database.GetPendingMigrations().Any()){defaultShardingDbContext.Database.Migrate();}} //如果需要在启动后扫描是否有表却扫了可以添加这个//app.Services.UseAutoTryCompensateTable();//......app.Run();折叠添加迁移文件Add-Migration Init启动程序分表分库自动迁移crud添加todo字段并迁移接下来我们将针对TodoItem添加一个name字段并且新增一张既不分库也不分表的表然后进行迁移public class TodoItem{public string Id { get; set; }public string Text { get; set; }public string Name { get; set; }
}
public class TodoTest{public string Id { get; set; }public string Test { get; set; }
}//docontextprotected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.EntityTodoItem(mb {mb.HasKey(o o.Id);mb.Property(o o.Id).IsRequired().HasMaxLength(50).HasComment(id);mb.Property(o o.Text).IsRequired().HasMaxLength(256).HasComment(事情);mb.Property(o o.Name).HasMaxLength(256).HasComment(姓名);mb.ToTable(nameof(TodoItem));});modelBuilder.EntityTodoTest(mb {mb.HasKey(o o.Id);mb.Property(o o.Id).IsRequired().HasMaxLength(50).HasComment(id);mb.Property(o o.Test).IsRequired().HasMaxLength(256).HasComment(测试);mb.ToTable(nameof(TodoTest));});}不出意外我们成功了然后再次启动启动程序后我们惊奇的发现不单原先的表新增了一个name字段并且为分片未分开的表也被添加进来了到此为止efcore的原生分库分表全自动化迁移Code-First已经全部完成,这不仅大大的提高了程序的性能并且大大的方便了开发人员的维护。集成AbpVNext完成了efcore原生的分表分库迁移我们将进行abp下的操作首先我们去github下的abp-samples里面下载对应的demo测试这边选择todo-mvc接着我们本地打开安装依赖,只需要安装·ShardingCore· 6.6.0.3。新建两个接口用于赋值创建时间和guid因为ShardingCore需要add,update,remove的时候shardingkey不可以为空,你可以自己赋值但是这样efcore的不分性能就不能用了//在TodoApp.Domain.Shared新增两个接口非必须public interface IShardingKeyIsCreationTime{}public class IShardingKeyIsGuId{}AbpDbContext抽象类因为Abp需要继承AbpDbContext所以这边进行一个修改因为ShardingCore只需要接口所以可以满足任何情况//为了篇幅移除了大部分代码剩下的可以在文末demo处查看public abstract class AbstractShardingAbpDbContextTDbContext : AbpDbContextTDbContext, IShardingDbContext, ISupportShardingReadWritewhere TDbContext : DbContext{private readonly IShardingDbContextExecutor _shardingDbContextExecutor;protected AbstractShardingAbpDbContext(DbContextOptionsTDbContext options) : base(options){var wrapOptionsExtension options.FindExtensionShardingWrapOptionsExtension(); if (wrapOptionsExtension ! null){_shardingDbContextExecutor new ShardingDbContextExecutor(this);}}public DbContext GetDbContext(string dataSourceName, CreateDbContextStrategyEnum strategy, IRouteTail routeTail){var dbContext _shardingDbContextExecutor.CreateDbContext(strategy, dataSourceName, routeTail); if (dbContext is AbpDbContextTDbContext abpDbContext abpDbContext.LazyServiceProvider null){abpDbContext.LazyServiceProvider this.LazyServiceProvider;} return dbContext;}}新增分库分表路由todoitem id取模分库public class TodoDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRouteTodoItem,string{public override string ShardingKeyToDataSourceName(object shardingKey){ if (shardingKey null) throw new InvalidOperationException(sharding key cant null);var stringHashCode ShardingCoreHelper.GetStringHashCode(shardingKey.ToString()); return $ds{(Math.Abs(stringHashCode) % 3)};//ds0,ds1,ds2}public override Liststring GetAllDataSourceNames(){ return new Liststring(){ ds0, ds1, ds2};}public override bool AddDataSourceName(string dataSourceName){throw new NotImplementedException();}public override void Configure(EntityMetadataDataSourceBuilderTodoItem builder){builder.ShardingProperty(o o.Id);}public override Funcstring, bool GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator){var t ShardingKeyToDataSourceName(shardingKey); switch (shardingOperator){ case ShardingOperatorEnum.Equal: return tail tail t; default:{ return tail true;}}}}todoitem text 取模分表public class TodoTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRouteTodoItem{public TodoTableRoute() : base(2, 5){}public override void Configure(EntityMetadataTableBuilderTodoItem builder){builder.ShardingProperty(o o.Text);}}编写sqlserver分片迁移脚本生成public class ShardingSqlServerMigrationsSqlGenerator: SqlServerMigrationsSqlGenerator{private readonly IShardingRuntimeContext _shardingRuntimeContext;public ShardingSqlServerMigrationsSqlGenerator(IShardingRuntimeContext shardingRuntimeContext,[NotNull] MigrationsSqlGeneratorDependencies dependencies, [NotNull] IRelationalAnnotationProvider migrationsAnnotations) : base(dependencies, migrationsAnnotations){_shardingRuntimeContext shardingRuntimeContext;}protected override void Generate(MigrationOperation operation,IModel model,MigrationCommandListBuilder builder){var oldCmds builder.GetCommandList().ToList();base.Generate(operation, model, builder);var newCmds builder.GetCommandList().ToList();var addCmds newCmds.Where(x !oldCmds.Contains(x)).ToList();MigrationHelper.Generate(_shardingRuntimeContext,operation, builder, Dependencies.SqlGenerationHelper, addCmds);}}abp的efcore模块注入TodoAppEntityFrameworkCoreModule编写注入public class TodoAppEntityFrameworkCoreModule : AbpModule{public static readonly ILoggerFactory efLogger LoggerFactory.Create(builder {builder.AddFilter((category, level) category DbLoggerCategory.Database.Command.Name level LogLevel.Information).AddConsole();});public override void PreConfigureServices(ServiceConfigurationContext context){TodoAppEfCoreEntityExtensionMappings.Configure();}public override void ConfigureServices(ServiceConfigurationContext context){context.Services.AddAbpDbContextTodoAppDbContext(options { /* Remove includeAllEntities: true to create* default repositories only for aggregate roots */options.AddDefaultRepositories(includeAllEntities: true);});ConfigureAbpDbContextOptions(options { /* The main point to change your DBMS.* See also TodoAppDbContextFactory for EF Core tooling. */options.UseSqlServer();options.ConfigureTodoAppDbContext(innerContext {ShardingCoreExtension.UseDefaultShardingTodoAppDbContext(innerContext.ServiceProvider, innerContext.DbContextOptions);});});context.Services.AddShardingConfigureTodoAppDbContext().UseRouteConfig(op {op.AddShardingDataSourceRouteTodoDataSourceRoute();op.AddShardingTableRouteTodoTableRoute();}).UseConfig((sp, op) { //var loggerFactory sp.GetRequiredServiceILoggerFactory();op.UseShardingQuery((conStr, builder) {builder.UseSqlServer(conStr).UseLoggerFactory(efLogger);});op.UseShardingTransaction((connection, builder) {builder.UseSqlServer(connection).UseLoggerFactory(efLogger);});op.UseShardingMigrationConfigure(builder {builder.ReplaceServiceIMigrationsSqlGenerator, ShardingSqlServerMigrationsSqlGenerator();});op.AddDefaultDataSource(ds0, Server.;DatabaseTodoApp;Trusted_ConnectionTrue);op.AddExtraDataSource(sp { return new Dictionarystring, string(){{ ds1, Server.;DatabaseTodoApp1;Trusted_ConnectionTrue },{ ds2, Server.;DatabaseTodoApp2;Trusted_ConnectionTrue }};});}).AddShardingCore();}public override void OnPostApplicationInitialization(ApplicationInitializationContext context){base.OnPostApplicationInitialization(context); //创建表的定时任务如果有按年月日系统默认路由的需要系统创建的记得开起来context.ServiceProvider.UseAutoShardingCreate(); //补偿表 //自动迁移的话不需要//context.ServiceProvider.UseAutoTryCompensateTable();}}折叠启动abp迁移项目启动等待输出插入todoitem查询验证到此为止我们这边完成了针对abpvnext的分表分库自动化迁移的操作集成Furion接下来我们开始集成Furion的操作首先依旧安装依赖添加依赖 ShardingCore 6.6.0.3 MySqlInstall-Package Furion -Version 3.7.5
//请安装最新版本目前x.6.0.5,第一个版本号6代表efcore的版本号
Install-Package ShardingCore -Version 6.6.0.5Install-Package Pomelo.EntityFrameworkCore.MySql -Version 6.0.1
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.6新增todoitempublic class TodoItem:IEntity, IEntityTypeBuilderTodoItem
{public string Id { get; set; }public string Text { get; set; }public void Configure(EntityTypeBuilderTodoItem entityBuilder, DbContext dbContext, Type dbContextLocator){entityBuilder.HasKey(o o.Id);entityBuilder.Property(o o.Id).IsRequired().HasMaxLength(50).HasComment(id);entityBuilder.Property(o o.Text).IsRequired().HasMaxLength(256).HasComment(事情);entityBuilder.ToTable(nameof(TodoItem));}
}新增带分片的DbContext和Abp一样抽象对象直接看远吗,这边直接新增一个dbcontextpublic class MyDbContext : AppShardingDbContextMyDbContext,IShardingTableDbContext
{public MyDbContext(DbContextOptionsMyDbContext options) : base(options){}public IRouteTail RouteTail { get; set; }
}新增分表分库路由新增分库路由public class TodoItemDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRouteTodoItem,string
{ /// summary/// id的hashcode取模余3分库/// /summary/// param nameshardingKey/param/// returns/returns/// exception crefInvalidOperationException/exceptionpublic override string ShardingKeyToDataSourceName(object shardingKey){ if (shardingKey null) throw new InvalidOperationException(sharding key cant null);var stringHashCode ShardingCoreHelper.GetStringHashCode(shardingKey.ToString()); return $ds{(Math.Abs(stringHashCode) % 3)};//ds0,ds1,ds2}private readonly Liststring _dataSources new Liststring() { ds0, ds1, ds2 };public override Liststring GetAllDataSourceNames(){ return _dataSources;}public override bool AddDataSourceName(string dataSourceName){throw new NotImplementedException();} /// summary/// id分库/// /summary/// param namebuilder/parampublic override void Configure(EntityMetadataDataSourceBuilderTodoItem builder){builder.ShardingProperty(o o.Id);}public override Funcstring, bool GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator){var t ShardingKeyToDataSourceName(shardingKey); switch (shardingOperator){ case ShardingOperatorEnum.Equal: return tail tail t; default:{ return tail true;}}}
}折叠新增分表路由public class TodoItemTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRouteTodoItem
{public TodoItemTableRoute() : base(2, 3){} /// summary/// 正常情况下不会用内容来做分片键因为作为分片键有个前提就是不会被修改/// /summary/// param namebuilder/parampublic override void Configure(EntityMetadataTableBuilderTodoItem builder){builder.ShardingProperty(o o.Text);}
}编写迁移文件using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal;
using Pomelo.EntityFrameworkCore.MySql.Migrations;
using ShardingCore.Core.RuntimeContexts;
using ShardingCore.Helpers;namespace TodoApp;public class ShardingMySqlMigrationsSqlGenerator:MySqlMigrationsSqlGenerator
{private readonly IShardingRuntimeContext _shardingRuntimeContext;public ShardingMySqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IRelationalAnnotationProvider annotationProvider, IMySqlOptions options,IShardingRuntimeContext shardingRuntimeContext) : base(dependencies, annotationProvider, options){_shardingRuntimeContext shardingRuntimeContext;}protected override void Generate(MigrationOperation operation,IModel model,MigrationCommandListBuilder builder){var oldCmds builder.GetCommandList().ToList();base.Generate(operation, model, builder);var newCmds builder.GetCommandList().ToList();var addCmds newCmds.Where(x !oldCmds.Contains(x)).ToList();MigrationHelper.Generate(_shardingRuntimeContext,operation, builder, Dependencies.SqlGenerationHelper, addCmds);}
}启动注入这边简单看了一下furion貌似没有提供FuncIServiceProvider,DbContextOptionBuilder的efcore注入方式所以这边不得已采用静态方式,如果采用静态的方式需要实现一个接口IDbContextCreator//静态创建IShardingRuntimeContextpublic class ShardingCoreProvider{private static ILoggerFactory efLogger LoggerFactory.Create(builder {builder.AddFilter((category, level) category DbLoggerCategory.Database.Command.Name level LogLevel.Information).AddConsole();});private static readonly IShardingRuntimeContext instance;public static IShardingRuntimeContext ShardingRuntimeContext instance; static ShardingCoreProvider(){instancenew ShardingRuntimeBuilderMyDbContext().UseRouteConfig(op {op.AddShardingTableRouteTodoItemTableRoute();op.AddShardingDataSourceRouteTodoItemDataSourceRoute();}).UseConfig((sp,op) {op.UseShardingQuery((con, b) {b.UseMySql(con, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);});op.UseShardingTransaction((con, b) {b.UseMySql(con, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);});op.AddDefaultDataSource(ds0, server127.0.0.1;port3306;databasefurion0;useridroot;passwordroot;);op.AddExtraDataSource(spnew Dictionarystring, string(){{ds1, server127.0.0.1;port3306;databasefurion1;useridroot;passwordroot;},{ds2, server127.0.0.1;port3306;databasefurion2;useridroot;passwordroot;}});op.UseShardingMigrationConfigure(b {b.ReplaceServiceIMigrationsSqlGenerator, ShardingMySqlMigrationsSqlGenerator();});}).ReplaceServiceIDbContextCreator, CustomerDbContextCreator(ServiceLifetime.Singleton).Build();}
}//启动服务public class ShardingCoreComponent:IServiceComponent
{public void Load(IServiceCollection services, ComponentContext componentContext){services.AddControllers();services.AddEndpointsApiExplorer();services.AddSwaggerGen();services.AddDatabaseAccessor(options { // 配置默认数据库options.AddDbMyDbContext(o {o.UseDefaultShardingMyDbContext(ShardingCoreProvider.ShardingRuntimeContext);});}); //依赖注入services.AddSingletonIShardingRuntimeContext(sp ShardingCoreProvider.ShardingRuntimeContext);}
}
public class CustomerDbContextCreator:ActivatorDbContextCreatorMyDbContext
{public override DbContext GetShellDbContext(IShardingProvider shardingProvider){var dbContextOptionsBuilder new DbContextOptionsBuilderMyDbContext();dbContextOptionsBuilder.UseDefaultShardingMyDbContext(ShardingCoreProvider.ShardingRuntimeContext); return new MyDbContext(dbContextOptionsBuilder.Options);}
}
public class UseShardingCoreComponent:IApplicationComponent
{public void Load(IApplicationBuilder app, IWebHostEnvironment env, ComponentContext componentContext){ //......app.ApplicationServices.UseAutoShardingCreate();var serviceProvider app.ApplicationServices;using (var scope app.ApplicationServices.CreateScope()){var defaultShardingDbContext scope.ServiceProvider.GetRequiredServiceMyDbContext(); if (defaultShardingDbContext.Database.GetPendingMigrations().Any()){defaultShardingDbContext.Database.Migrate();}} // app.Services.UseAutoTryCompensateTable();}
}//Programusing TodoApp;Serve.Run(RunOptions.Default.AddComponentShardingCoreComponent().UseComponentUseShardingCoreComponent());折叠添加迁移文件启动增删改查集成WTM之前也有一次继承过之后也有因为迁移过于麻烦所以这边ShardingCore出了更加完善迁移方案并且使用起来code-first更加无感添加依赖添加依赖 ShardingCore 6.6.0.3 MySql//请安装最新版本目前x.6.0.5,第一个版本号6代表efcore的版本号
Install-Package ShardingCore -Version 6.6.0.5
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.6新增分表分库路由//分库路由public class TodoDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRouteTodo,string
{ /// summary/// id的hashcode取模余3分库/// /summary/// param nameshardingKey/param/// returns/returns/// exception crefInvalidOperationException/exceptionpublic override string ShardingKeyToDataSourceName(object shardingKey){ if (shardingKey null) throw new InvalidOperationException(sharding key cant null);var stringHashCode ShardingCoreHelper.GetStringHashCode(shardingKey.ToString()); return $ds{(Math.Abs(stringHashCode) % 3)};//ds0,ds1,ds2}private readonly Liststring _dataSources new Liststring() { ds0, ds1, ds2 };public override Liststring GetAllDataSourceNames(){ return _dataSources;}public override bool AddDataSourceName(string dataSourceName){throw new NotImplementedException();} /// summary/// id分库/// /summary/// param namebuilder/parampublic override void Configure(EntityMetadataDataSourceBuilderTodo builder){builder.ShardingProperty(o o.Id);}public override Funcstring, bool GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator){var t ShardingKeyToDataSourceName(shardingKey); switch (shardingOperator){ case ShardingOperatorEnum.Equal: return tail tail t; default:{ return tail true;}}}
}//分表路由public class TodoTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRouteTodo
{public TodoTableRoute() : base(2, 3){} /// summary/// 正常情况下不会用内容来做分片键因为作为分片键有个前提就是不会被修改/// /summary/// param namebuilder/parampublic override void Configure(EntityMetadataTableBuilderTodo builder){builder.ShardingProperty(o o.Name);}
}折叠创建DbContextCreatorpublic class WTMDbContextCreator:IDbContextCreator
{public DbContext CreateDbContext(DbContext shellDbContext, ShardingDbContextOptions shardingDbContextOptions){var context new DataContext((DbContextOptionsDataContext)shardingDbContextOptions.DbContextOptions);context.RouteTail shardingDbContextOptions.RouteTail; return context;}public DbContext GetShellDbContext(IShardingProvider shardingProvider){var dbContextOptionsBuilder new DbContextOptionsBuilderDataContext();dbContextOptionsBuilder.UseDefaultShardingDataContext(ShardingCoreProvider.ShardingRuntimeContext); return new DataContext(dbContextOptionsBuilder.Options);}
}迁移脚本public class ShardingMySqlMigrationsSqlGenerator:MySqlMigrationsSqlGenerator
{private readonly IShardingRuntimeContext _shardingRuntimeContext;public ShardingMySqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IRelationalAnnotationProvider annotationProvider, IMySqlOptions options,IShardingRuntimeContext shardingRuntimeContext) : base(dependencies, annotationProvider, options){_shardingRuntimeContext shardingRuntimeContext;}protected override void Generate(MigrationOperation operation,IModel model,MigrationCommandListBuilder builder){var oldCmds builder.GetCommandList().ToList();base.Generate(operation, model, builder);var newCmds builder.GetCommandList().ToList();var addCmds newCmds.Where(x !oldCmds.Contains(x)).ToList();MigrationHelper.Generate(_shardingRuntimeContext,operation, builder, Dependencies.SqlGenerationHelper, addCmds);}
}静态构造IShardingRuntimeContext因为WTM在创建dbcontext并不是通过依赖注入创建的而是由其余的内部实现所以为了兼容我们这边只能通过静态IShardingRuntimeContext注入public class ShardingCoreProvider{private static ILoggerFactory efLogger LoggerFactory.Create(builder {builder.AddFilter((category, level) category DbLoggerCategory.Database.Command.Name level LogLevel.Information).AddConsole();});private static readonly IShardingRuntimeContext instance;public static IShardingRuntimeContext ShardingRuntimeContext instance; static ShardingCoreProvider(){instancenew ShardingRuntimeBuilderDataContext().UseRouteConfig(op {op.AddShardingTableRouteTodoRoute();op.AddShardingDataSourceRouteTodoDataSourceRoute();}).UseConfig((sp,op) {op.UseShardingQuery((con, b) {b.UseMySql(con, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);});op.UseShardingTransaction((con, b) {b.UseMySql(con, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);});op.AddDefaultDataSource(ds0, server127.0.0.1;port3306;databasewtm0;useridroot;passwordroot;);op.AddExtraDataSource(spnew Dictionarystring, string(){{ds1, server127.0.0.1;port3306;databasewtm1;useridroot;passwordroot;},{ds2, server127.0.0.1;port3306;databasewtm2;useridroot;passwordroot;}});op.UseShardingMigrationConfigure(b {b.ReplaceServiceIMigrationsSqlGenerator, ShardingMySqlMigrationsSqlGenerator();});}).ReplaceServiceIDbContextCreator, WTMDbContextCreator(ServiceLifetime.Singleton).Build();}
}折叠创建抽象分片DbContext因为过于长所以这边只显示主要部分其余通过demo查看public abstract class AbstractShardingFrameworkContext:FrameworkContext, IShardingDbContext, ISupportShardingReadWrite{protected IShardingDbContextExecutor ShardingDbContextExecutor{get;}public AbstractShardingFrameworkContext(CS cs): base(cs){ShardingDbContextExecutor new ShardingDbContextExecutor(this);IsExecutor false;}public AbstractShardingFrameworkContext(string cs, DBTypeEnum dbtype): base(cs, dbtype){ShardingDbContextExecutor new ShardingDbContextExecutor(this);IsExecutor false;}public AbstractShardingFrameworkContext(string cs, DBTypeEnum dbtype, string version null): base(cs, dbtype, version){ShardingDbContextExecutor new ShardingDbContextExecutor(this);IsExecutor false;}public AbstractShardingFrameworkContext(DbContextOptions options) : base(options){var wrapOptionsExtension options.FindExtensionShardingWrapOptionsExtension(); if (wrapOptionsExtension ! null){ShardingDbContextExecutor new ShardingDbContextExecutor(this);;}IsExecutor wrapOptionsExtension null;}protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ if (this.CSName!null){base.OnConfiguring(optionsBuilder);optionsBuilder.UseDefaultShardingDataContext(ShardingCoreProvider.ShardingRuntimeContext);}}public DbContext GetDbContext(string dataSourceName, CreateDbContextStrategyEnum strategy, IRouteTail routeTail){ return ShardingDbContextExecutor.CreateDbContext(strategy, dataSourceName, routeTail);}
}折叠修改dbcontextpublic class DataContextFactory : IDesignTimeDbContextFactoryDataContext{public DataContext CreateDbContext(string[] args){var virtualDataSource ShardingCoreProvider.ShardingRuntimeContext.GetVirtualDataSource();var defaultConnectionString virtualDataSource.DefaultConnectionString; return new DataContext(defaultConnectionString, DBTypeEnum.MySql);}}注入ShardingCore移除掉了之前的多余代码public void ConfigureServices(IServiceCollection services){ //....services.AddSingletonIShardingRuntimeContext(sp ShardingCoreProvider.ShardingRuntimeContext);}public void Configure(IApplicationBuilder app, IOptionsMonitorConfigs configs){IconFontsHelper.GenerateIconFont(); // using (var scope app.ApplicationServices.CreateScope())// {// var requiredService scope.ServiceProvider.GetRequiredServiceWTMContext();// var requiredServiceDc requiredService.DC;// }//定时任务app.ApplicationServices.UseAutoShardingCreate();using (var dbconContextnew DataContextFactory().CreateDbContext(new string[0])){dbconContext.Database.Migrate();} //补齐表防止iis之类的休眠导致按天按月的表没有新建//app.ApplicationServices.UseAutoTryCompensateTable();//....}迁移启动程序crud最后的最后(ShardingWithFrameWork)[https://github.com/xuejmnet/ShardingWithFramework] https://github.com/xuejmnet/ShardingWithFramework您都看到这边了确定不点个star或者赞吗,一款.Net不得不学的分库分表解决方案,简单理解为sharding-jdbc在.net中的实现并且支持更多特性和更优秀的数据聚合,拥有原生性能的97%,并且无业务侵入性,支持未分片的所有efcore原生查询github地址 https://github.com/xuejmnet/sharding-coregitee地址 https://gitee.com/dotnetchina/sharding-core