ABP EF CORE 7 集成ShardingCore实现分表
参考文章
Abp VNext 分表分库
.Net下极限生产力之分表分库全自动化Migrations Code-First
官方文档
ShardingCore
原文是在Abp VNext的基础上做了分库分表,本文将基于ABP和EF CORE 7做分表,支持分表和批量更新的系统性能应该会有一个较大的提升。
添加依赖
Install-Package ShardingCore -Version 7.0.0.5
创建实体
public class ToDoItem : AggregateRoot<long>, ISoftDelete, IMayHaveTenant, ICreationAudited, IShardingKeyIsCreationTime
{
public bool IsDeleted { get; set; }
public int? TenantId { get; set; }
public long? CreatorUserId { get; set; }
public DateTime CreationTime { get; set; }
public string Text { get; set; }
}
//标识对应的分表对像的分表字段是id时,自动创建guid
public interface IShardingKeyIsGuId
{
}
//标识对应的分表对象的分表字段是创建时间时,自动创建时间
public interface IShardingKeyIsCreationTime
{
}
参考博客中的分表是基于Id做的,那我就试一下基于时间做分表。
正常来说新增实体的话只需要写一个ToDoItem 类就可以了,但参考博客中还定义了两个空接口,那么这两个空接口的作用是什么呢?
从这段代码中我们可以看出,这两个空接口可以帮你自动创建分表字段的值。
其实参考博客中也注释了这两个接口的作用:
我在我的实体代码中修改了这个注释,不知道我的理解对不对。
创建DbContext抽象类
参考博客中使用的是AbstractShardingAbpDbContext这个类,它是继承于AbpDbContext的。但我的ABP的DbContext是继承于AbpZeroDbContext的。于是我找到了ShardingDbContext抽象类的源码
将AbstractShardingAbpZeroDbContext添加到我的项目中后,并继承AbstractShardingAbpZeroDbContext:
public class MyABP7NET6DbContext : AbstractShardingAbpZeroDbContext<Tenant, Role, User, MyABP7NET6DbContext>
{
/* Define a DbSet for each entity of the application */
public virtual DbSet<ToDoItem> ToDoItems { get; set; }
public MyABP7NET6DbContext(DbContextOptions<MyABP7NET6DbContext> options)
: base(options)
{
}
}
创建分表路由
public class TodoTableRoute : AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<ToDoItem>
{
//分表的开始时间
public override DateTime GetBeginTime()
{
return new DateTime(2022, 10, 1);
}
//注意一定要配置或者采用接口+标签也是可以的
public override void Configure(EntityMetadataTableBuilder<ToDoItem> builder)
{
//创建时间按月分表
builder.ShardingProperty(o => o.CreationTime);
}
public override bool AutoCreateTableByTime()
{
return true;
}
}
startup配置
这里我把数据库连接字符串改成了获取配置文件的。 // 添加分片配置
services.AddShardingDbContext<MyABP7NET6DbContext>()
.UseRouteConfig(op =>
{
op.AddShardingTableRoute<TodoTableRoute>();
}).UseConfig((sp, op) =>
{
op.UseShardingQuery((conn, builder) =>
{
builder.UseSqlServer(conn);
});
op.UseShardingTransaction((conn, builder) =>
{
builder.UseSqlServer(conn);
});
op.AddDefaultDataSource(
Guid.NewGuid().ToString("n"),
_appConfiguration["ConnectionStrings:Default"]//"Data Source=localhost;Initial Catalog=EFCoreShardingTableDB;Integrated Security=True;"
);
}).AddShardingCore();
// sharding-core
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// not required, enable check table missing and auto create,非必须 启动检查缺少的表并且创建
app.ApplicationServices.UseAutoTryCompensateTable();
创建Controller
需要注入DbContext
[Route("api/[controller]/[action]")]
public class ToDoItemController : MyABP7NET6ControllerBase
{
private readonly MyABP7NET6DbContext _dbContext;
public ToDoItemController(
MyABP7NET6DbContext dbContext
)
{
_dbContext = dbContext;
}
[HttpPost]
public async Task Add()
{
var msg = "测试数据";
var currentTime = DateTime.Now;
var list = await _dbContext.Set<ToDoItem>().ToListAsync();//如果数据中含有不符合路由条件的数据,会报错
Console.WriteLine($"新增前数量:{list.Count}");
await _dbContext.Set<ToDoItem>().AddAsync(new ToDoItem()
{
CreationTime = currentTime,
Text = msg
});
await _dbContext.SaveChangesAsync();
list = await _dbContext.Set<ToDoItem>().ToListAsync();
Console.WriteLine($"新增后数量:{list.Count}");
}
[HttpPost]
public async Task UpdateBatch()
{
_dbContext.Set<ToDoItem>().AsQueryable().ExecuteUpdate(update => update.SetProperty(x => x.CreationTime, x => x.CreationTime.AddYears(-10)));
}
}
创建数据库迁移
PM> Add-Migration Add_ToDoItem
PM> update-database
因为在分表路由中配置了开始分表时间是2022-10-01,我现在是11月,所以框架自动创建了10月和11月两个表。
测试
调用Add和UpdateBatch接口可以发现数据按照CreationTime自动分表了。
需要注意的是,使用EF CORE 7 的 ExecuteUpdate如果不写时间条件的话,会更新所有分表。