NetCore框架WTM的分表分庫實現

介紹

本期主角:

  • ShardingCore 一款ef-core下高性能、輕量級針對分表分庫讀寫分離的解決方案,具有零依賴、零學習成本、零業務代碼入侵
  • WTM WalkingTec.Mvvm框架(簡稱WTM)是基於.net core的快速開發框架。支持Layui(前後端不分離), React(前後端分離),VUE(前後端分離),內置代碼生成器,最大程度的提高開發效率,是一款高效開發的利器。

ShardingCore最新版本針對路由有了極大性能的優化由原先的Expression改成自定義的RouteExpression去除了Compile帶來的性能損耗

我不是efcore怎麼辦

這邊肯定有小夥伴要問有沒有不是efcore的,我這邊很確信的和你講有並且適應所有的ADO.NET包括sqlhelper
ShardingConnector 一款基於ado.net下的高性能分表分庫解決方案目前已有demo案例,這個框架你可以認爲是.Net版本的ShardingSphere但是目前僅實現了ShardingSphere-JDBC,後續我將會實現ShardingSphere-Proxy希望各位.Neter多多關注

背景

之前我不是發了一篇博客嗎.Net分表分庫動態化處理 下面有個小夥伴留言,希望可以讓我支持一下WTM 框架。我心想着處於對自己的框架的自信,並且之前有過對abpvnexfurion等一系列框架的兼容適配的嘗試,原則上將只要你是efcore那麼基本上都可以支持,所以秉着嘗試以下的態度這邊就上手了,先說下結論就是可以支持,完美不完美不清楚因爲本人這個框架用的不多不知道是否是完美適配。

原理

ShardingCore

ShardingCore的整體架構是一個殼dbcontext帶多個dbcontext,殼dbcontext不進行增刪改查,由內部的dbcontext自己去執行,這個因爲efcore的一個對象對應一個表所限制的。我們這邊把殼dbcontext稱作shellDbContext,執行的dbcontext叫做executorDbContext,對於ShardingCore還有一個要求就是需要初始化啓動的時候Start()Start()內部需要IServiceProvider來獲取DbContext,所以說整個框架離不開ioc,那麼就需要啓動的時候依賴注入DbContext,又因爲依賴注入如果是默認的只能允許單個構造函數。這就是ShardingCore在兼容使用的時候需要注意的地方。

WTM

WTM這邊我不是很熟悉,花了大概半個小時到一個小時左右的時間,進行了代碼的翻閱,大概瞭解了其中的實現,DbContext的創建由獨立的構造函數來實現,默認通過DbContext的內部方法 OnConfiguring(DbContextOptionsBuilder optionsBuilder)來進行初始化,框架裏面將DbContext抽象成了IDataContext接口,框架默IDataContext接口默認依賴注入爲NullDbContext如果需要使用會自行通過反射調用構造函數參數爲CS類型的那一個。整體的efcore上的一些處理通過調試代碼和源碼的查看基本上了解了

開始接入

創建項目

那麼我們首先通過WTM生成一個腳手架的簡單項目,這邊生成了一個mvc的項目。

添加依賴

添加ShardingCore依賴,需要x.5.0.6+版本,x代表efcore的版本

Install-Package ShardingCore -Version 6.5.0.6

添加抽象分表DbContext

這邊和AbpVNext時候繼承一樣,因爲c#不支持多繼承,好在ShardingCore是接口依賴不存在實現依賴所以任何框架都可以兼容。


    public abstract class AbstractShardingFrameworkContext:FrameworkContext, IShardingDbContext, ISupportShardingReadWrite
    {
        protected IShardingDbContextExecutor ShardingDbContextExecutor
        {
            get;
        }

        public AbstractShardingFrameworkContext(CS cs)
            : base(cs)
        {
            
            ShardingDbContextExecutor =
                (IShardingDbContextExecutor)Activator.CreateInstance(
                    typeof(ShardingDbContextExecutor<>).GetGenericType0(this.GetType()),this);
            IsExecutor = false;
        }
        
        public AbstractShardingFrameworkContext(string cs, DBTypeEnum dbtype)
            : base(cs, dbtype)
        {
            ShardingDbContextExecutor =
                (IShardingDbContextExecutor)Activator.CreateInstance(
                    typeof(ShardingDbContextExecutor<>).GetGenericType0(this.GetType()),this);
            IsExecutor = false;
        }
        
        public AbstractShardingFrameworkContext(string cs, DBTypeEnum dbtype, string version = null)
            : base(cs, dbtype, version)
        {
            ShardingDbContextExecutor =
                (IShardingDbContextExecutor)Activator.CreateInstance(
                    typeof(ShardingDbContextExecutor<>).GetGenericType0(this.GetType()),this);
            IsExecutor = false;
        }

        public AbstractShardingFrameworkContext(DbContextOptions options) : base(options)
        {
            var wrapOptionsExtension = options.FindExtension<ShardingWrapOptionsExtension>();
            if (wrapOptionsExtension != null)
            {
                ShardingDbContextExecutor =
                    (IShardingDbContextExecutor)Activator.CreateInstance(
                        typeof(ShardingDbContextExecutor<>).GetGenericType0(this.GetType()),this);
            }

            IsExecutor = wrapOptionsExtension == null;
        }
        
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (this.CSName!=null)
            {
                base.OnConfiguring(optionsBuilder);
                optionsBuilder.UseSharding<DataContext>();
            }
        }
        
    }

簡單說一下這邊實現了WTM的所有構造函數,因爲ShardingCore原生需要DbContextOption,當然也是可以支持實現類由自定義DbContext,構造函數中如果使用了DbContextOption那麼就是由依賴注入或者ShardingCore創建的DbContext,其餘的全部是WTM創建的,所以這邊都需要實現並且其餘的構造函數直接設置爲ShellDbContext

又因爲WTM默認的創建會賦值CSName所以需要對其後續進行UseSharding處理這是ShardingCore針對ShellDbContext必須要處理的


        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (this.CSName!=null)
            {
                base.OnConfiguring(optionsBuilder);
                optionsBuilder.UseSharding<DataContext>();
            }
        }

實現DataContext

很簡單隻需要繼承抽象類和實現IShardingTableDbContext接口即可,實現該接口才能支持分表否則僅支持分庫

 public class DataContext : AbstractShardingFrameworkContext,IShardingTableDbContext
{
}

編寫自定義DbContext創建

因爲WTM框架的DbContext擁有多個構造函數所以需要自定義,由ShardingCore提供

代碼其實很簡單就是如何創建一個DbContext,因爲ShardingCore默認的會校驗只能擁有一個構造函數並且構造函數只能是DbContextOptions或者DbContextOptions<>

public class WTMDbContextCreator<TShardingDbContext>:IDbContextCreator<TShardingDbContext>  where TShardingDbContext : DbContext, IShardingDbContext
{
    public DbContext CreateDbContext(DbContext shellDbContext, ShardingDbContextOptions shardingDbContextOptions)
    {
        var context = new DataContext((DbContextOptions<DataContext>)shardingDbContextOptions.DbContextOptions);
        context.RouteTail = shardingDbContextOptions.RouteTail;
        return context;
    }
}

編寫分表測試類

    public class Todo
    {
    public string Id { get; set; }
    public string Name { get; set; }
    }

然後再DbContext出簡單設置一下

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //你用dbset也是可以的
            modelBuilder.Entity<Todo>(e =>
            {
                e.HasKey(o => o.Id);
                e.ToTable(nameof(Todo));
            });
        }

添加分表路由


    public class TodoRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<Todo>
    {
        public TodoRoute() : base(2, 10)
        {
        }

        public override void Configure(EntityMetadataTableBuilder<Todo> builder)
        {
            builder.ShardingProperty(o => o.Id);
        }
    }

StartUp

接下來就是激動人心的時候了,首先我們說過ShardingCore需要依賴注入,由因爲DbContext是多構造函數

services.AddScoped<DataContext>(sp =>
            {
                var dbContextOptionsBuilder = new DbContextOptionsBuilder<DataContext>();
                dbContextOptionsBuilder.UseMySql(
                    "server=127.0.0.1;port=3306;database=shardingTest;userid=root;password=root;",
                    new MySqlServerVersion(new Version()));
                dbContextOptionsBuilder.UseSharding<DataContext>();
                return new DataContext(dbContextOptionsBuilder.Options);
            });

注意依賴注入獲取的是ShellDbContext所以我們需要對其進行UseSharding()

再來我們需要配置ShardingCore

services.AddShardingConfigure<DataContext>()
                .AddEntityConfig(o =>
                {
                    o.CreateShardingTableOnStart = true;
                    o.EnsureCreatedWithOutShardingTable = true;
                    o.AddShardingTableRoute<TodoRoute>();
                })
                .AddConfig(o =>
                {
                    o.AddDefaultDataSource("ds0",
                        "server=127.0.0.1;port=3306;database=shardingTest;userid=root;password=root;");
                    o.ConfigId = "c1";
                    o.UseShardingQuery((conn, build) =>
                    {
                        build.UseMySql(conn, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);
                    });
                    o.UseShardingTransaction((conn,build)=>
                        build.UseMySql(conn,new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger)
                        );
                    o.ReplaceTableEnsureManager(sp => new MySqlTableEnsureManager<DataContext>());
                }).EnsureConfig();

這邊的配置就是ShardingCore很簡單可以查詢文檔或者過往的博客

這個時候有人要說了爲什麼不使用AddShardingDbContext因爲多構造函數默認不支持需要手動處理。

替換ShardingCoreDbContext創建,我們剛纔寫的

   services.Replace(ServiceDescriptor.Singleton<IDbContextCreator<DataContext>, WTMDbContextCreator<DataContext>>());

再然後替換WTMIDataContext

//這是WTM的默認的需要替換掉
//services.TryAddScoped<IDataContext, NullContext>();
  services.Replace(ServiceDescriptor.Scoped<IDataContext>(sp =>
            {
                return sp.GetService<DataContext>();
            }));

然後啓動初始化ShardingCore

            app.ApplicationServices.GetRequiredService<IShardingBootstrapper>().Start();

編寫測試demo

  public async Task<ActionResult> Login(LoginVM vm)
        {
            var dataContext = Wtm.DC;
            var todos = new List<Todo>();
            for (int i = 0; i < 100; i++)
            {
                var todo = new Todo();
                todo.Id = Guid.NewGuid().ToString("n");
                todo.Name = todo.Id;
                todos.Add(todo);
            }

            await dataContext.Set<Todo>().AddRangeAsync(todos);
            await dataContext.SaveChangesAsync();

            var listAsync = await dataContext.Set<Todo>().Take(2).ToListAsync();
....
}

啓動運行

完美創建分表並且可以插入查詢完全和使用WTM一樣

最後的最後

demo地址 https://github.com/xuejmnet/ShardingWTM

您都看到這邊了確定不點個star或者贊嗎,一款.Net不得不學的分庫分表解決方案,簡單理解爲sharding-jdbc在.net中的實現並且支持更多特性和更優秀的數據聚合,擁有原生性能的97%,並且無業務侵入性,支持未分片的所有efcore原生查詢

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章