Furion分表分库我也要happy coding

Furion分表分库集成ShardingCore

ShardingCore

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


Github Star 助力dotnet 生态 Gitee Star


背景

随着.net6的发布,陆陆续续的框架都已经开始支持了,ShardingCore也不例外,为了推动.net生态,也为了让更多的的人了解.net下其实也有分表分库的解决方案,所以打算写一篇关于ShardingCore集成到其他框架的思路。

Fourin框架大家应该也是比较清楚的,作者也是一个乐于开源有着开源精神的人,秉着大家都是热爱开源的所以决定助Fourin一臂之力(蹭一下热度)。随着上次的AbpVNext的”完美”集成ShardingCore后,这次带来的就是Furion下的ShardingCore集成。

efcore下完美的分表分库解决方案,支持任意efcore的集成框架,完美支持或者需要稍作修改即可完美支持,并不需要让你有任何使用的学习成本,几乎做到不修改现有efcore代码的前提下让你的程序完美支持分表分库。真正做到“零”代码修改。

新建Furion项目

首先我们已目前Furion最新版本为例v3.0.5,新建一个空的aspnetcore web api的net6项目,当然也支持efcore2+的所有aspnetcore版本。

然后我们添加ShardingCoreefcore.SqlServer

新建dbcontext

这边看了Furion的使用方法和Abp一样需要继承一个他自己的DbContext叫做AppDbContext,但是没有关西。因为ShardingCore是基于接口来实现的,所以不存在多继承问题,只是我们需要自行实现Furion的ShardingDbContext的抽象版本源码地址 AppShardingDbContext

简单的说下就是如果你是需要继承某个dbcontext的那么就需要自己实现三个接口IShardingDbContext, ISupportShardingTransaction, ISupportShardingReadWrite分别是分表分库的核心接口,支持事务,支持读写分离。

具体已经帮你们抽象好了只需要复制代码就可以了。

做好了准备工作我们开始新建dbcontext

[AppDbContext("SqlServerConnectionString", DbProvider.SqlServer)]
public class DefaultDbContext : AppShardingDbContext<DefaultDbContext>,IShardingTableDbContext
{
    public DefaultDbContext(DbContextOptions<DefaultDbContext> options) : base(options)
    {
    }

    public IRouteTail RouteTail { get; set; }
}

创建todoitem对象

public class TodoItem:IPrivateEntity
{
    public string Id { get; set; }
    public string Name { get; set; }
}

  

这边我们将TodoItemId做成取模分表

配置dbcontext

[AppDbContext("SqlServerConnectionString", DbProvider.SqlServer)]
public class DefaultDbContext : AppShardingDbContext<DefaultDbContext>,IShardingTableDbContext
{
    public DefaultDbContext(DbContextOptions<DefaultDbContext> options) : base(options)
    {
    }

    public IRouteTail RouteTail { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<TodoItem>(entity =>
        {
            entity.HasKey(o => o.Id);
            entity.Property(o => o.Id).IsRequired().IsUnicode(false).HasMaxLength(50).HasComment("Id");
            entity.Property(o => o.Name).IsRequired().HasMaxLength(50).HasComment("名称");
            entity.ToTable(nameof(TodoItem));
        });
    }
}

  

配置了todoitem的简单配置,注意这边不一定要这么配置,也可以用Attribute+DbSet,也可以用FluentApi

创建TodoItem的分表路由

public class TodoItemVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualTableRoute<TodoItem>
{
    public TodoItemVirtualTableRoute() : base(2, 8)
    {
    }
    public override void Configure(EntityMetadataTableBuilder<TodoItem> builder)
    {
        builder.ShardingProperty(x => x.Id);
    }
}

  这边采用的是简单取模通过构造函数传入,具体可以参考文档就是分表后缀为2位数,模8也就是00,01…..07,一共8张表,ShardingProperty(x => x.Id)表示TodoItem的分表字段为Id。

注意: 这边只是为了简单演示,你如果需要外部传入可以自行通过实现AbstractShardingOperatorVirtualTableRoute来实现,并且路由构造函数支持单例的依赖注入

初始化Furion配置

AppStartup

[AppStartup(600)]
public sealed class SqlServerEntityFrameworkCoreStartup : AppStartup
{
    public static readonly ILoggerFactory efLogger = LoggerFactory.Create(builder =>
    {
        builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
    });
    public void ConfigureServices(IServiceCollection services)
    {
        var connStr = DbProvider.GetConnectionString<DefaultDbContext>(/*这里可写可不写*/);
        // 配置数据库上下文,支持N个数据库
        services.AddDatabaseAccessor(options =>
        {
            // 配置默认数据库
            options.AddDb<DefaultDbContext>(o =>
            {
                o.UseSqlServer(connStr).UseSharding<DefaultDbContext>().UseLoggerFactory(efLogger);
            });

        });
        services.AddShardingConfigure<DefaultDbContext>((s, builder) =>
        {
            builder.UseSqlServer(s).UseLoggerFactory(efLogger);
        }).Begin(o =>
        {
            o.CreateShardingTableOnStart = true;
            o.EnsureCreatedWithOutShardingTable = true;
            o.AutoTrackEntity = true;
        })
             .AddShardingTransaction((connection, builder) =>
                 builder.UseSqlServer(connection).UseLoggerFactory(efLogger))
             .AddDefaultDataSource("ds0", connStr)
             .AddShardingTableRoute(o =>
             {
                 o.AddShardingTableRoute<TodoItemVirtualTableRoute>();
             }).End();
    }
}

  通过官网我们可以知道如何针对Furion进行efcore的配置,这边目前sharding-core不支持efcore的单例dbcontext,所以不建议使用单例。

额外配置:.UseSharding<DefaultDbContext>()仅需要配置dbcontext使用sharding原先的所有dbcontext的注入都可以不去管他

sharding-core的配置:AddShardingConfiguresharding-core在efcore的基础上额外进行配置就可以支持分表分库的扩展更多配置可以参考文档

Program

using ShardingCore.Bootstrapers;

var builder = WebApplication.CreateBuilder(args).Inject();

// Add services to the container.

builder.Services.AddControllers().AddInject();

var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseAuthorization();

app.UseInject();
app.Services.GetRequiredService().Start();
app.MapControllers();

app.Run();
 注意:很多同学老是忘记启动导致sharding-core没法使用app.Services.GetRequiredService<IShardingBootstrapper>().Start(); 

编写控制器

首先我们通过注入构造函数IRepository<TodoItem> todoItemRepository这是Furion提供的

其次我们编写增删改查接口

    [HttpGet]
    public async Task<IActionResult> Add()
    {
        await _todoItemRepository.InsertAsync(new TodoItem() { Id = Guid.NewGuid().ToString("n"), Name = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") });
        await _todoItemRepository.SaveNowAsync();
        return Ok();
    }
    [HttpGet]
    public async Task<IActionResult> List()
    {
        var list = await _todoItemRepository.AsQueryable().ToListAsync();
        return Ok(list);
    }
    [HttpGet]
    public async Task<IActionResult> First([FromQuery]string id)
    {
        var todoItem = await _todoItemRepository.FirstOrDefaultAsync(o => o.Id == id);
        return Ok(todoItem);
    }
    [HttpGet]
    public async Task<IActionResult> Update([FromQuery]string id)
    {
        var todoItem = await _todoItemRepository.FirstOrDefaultAsync(o => o.Id == id);
        todoItem.Name = "123";
        await _todoItemRepository.SaveNowAsync();
        return Ok(todoItem);
    }

启动项目

因为默认为了测试ShardingCore配置了

o.CreateShardingTableOnStart = true;
o.EnsureCreatedWithOutShardingTable = true;
 所以启动会帮我们建表并且建库

新增

符合预期插入到了具体的hash%8的表

查询所有

因为是获取所有所以这边会查询所有的分表符合预期

获取单条

获取单条符合预期用的id取模所以用id去查询会到指定的表里面

更新

符合预期代码先查询05表在更新05表并且使用的是追踪更新

我们接着再来查询一次看看是否真的修改了

到此为止我们的FurionShardingCore集成就完成了,当然这只是ShardingCore的冰山一角,最最最简单的分表,如果你喜欢或者你认为ShardingCore有用那么可以给个赞或者star吗?开原作者希望自己开源的项目被更多人高人指点并且进步。也希望为.net下的分表分库进行一份微薄之力。在efcore下我相信ShardingCore是在官方不支持efcore下最最最完美的解决方案。全程的使用代码体验是0感知,只要你配置好了完全不需要考虑,当然也有同学要杠,如果某些情况下我就是要指定某几张表呢,ShardingCore也是支持的所以在自动情况下是“零”入侵业务代码的最优分表分库方案,更多使用方式请查询

github ShardingCore文档

gitee ShardingCore文档

最后项目源码

FurionShardingDemo

dotnet 天下第一 (大吼)

分表分库组件求赞求star


博客

QQ群:771630778

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

个人邮箱:[email protected]