ABP EF CORE 7 集成ShardingCore实现分表

参考文章
Abp VNext 分表分库
.Net下极限生产力之分表分库全自动化Migrations Code-First
官方文档
ShardingCore

原文是在Abp VNext的基础上做了分库分表,本文将基于ABPEF 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如果不写时间条件的话,会更新所有分表。

posted @ 2022-11-27 11:36  cnblogsName  阅读(296)  评论(0编辑  收藏  举报