第三十三节:基于ShardingCore框架分库、分库分表实操

一. 只分库-实操

1. 目标

    有三个DB,分别是ShardingDB_00、ShardingDB_01、ShardingDB_02,每个DB中都有两张表:Order、SysUser。

    (1). Order表中的数据,按照用户区域字段Area来分库。

    (2). SysUser表中的数据,也是按照用户区域字段Area来分库。

 PS:Area字段是string类型,所以需要自己设计分库算法, 我们这里的算法为:先对字段求hashcode,然后对 3 取模,得到 0、1、2, 正好存放到xx_00、xx_01、xx_02 数据库中。

 

2. 实操步骤

(这里只分享核心代码,表实体代码、EFCore上下文代码 不再重复)

(1).  分库路由

   因为分库仅提供默认路由,所以需要用户自行实现AbstractShardingOperatorVirtualDataSourceRoute抽象, 分库逻辑需要在重写的 ShardingKeyToDataSourceName 方法中实现。

   分库原理:   

        A. 这里靠的是返回数据源的名称,返回00,则对应数据源名称为00,这个shardingkey则进入00数据源对应的DB

        B. 00 指的是program中定义的名称,也就是这里的_dataSources

        C. 至于 名称00 对应的DB的真实名称是什么,不是很关键,可以叫 ShardingDB_00 ,也可以叫 ShardingDB_AAAA

代码分享:

/// <summary>
/// Order表按照用户区域字段Area来分库
/// 因为分库仅提供默认路由,所以需要用户自行实现AbstractShardingOperatorVirtualDataSourceRoute抽象
/// </summary>
public class OrderVirtualDataSourceRoute : AbstractShardingOperatorVirtualDataSourceRoute<Order, string>
{

    ////数据源名称--需要和program中的名称对应
    //private readonly List<string> _dataSources = ["A", "B", "C"];

    ////我们设置区域就是数据库
    //public override string ShardingKeyToDataSourceName(object shardingKey)
    //{
    //    return shardingKey?.ToString() ?? string.Empty;
    //}




    //数据源名称--需要和program中的名称对应
    private readonly List<string> _dataSources = ["00", "01", "02"];


    /// <summary>
    /// 自定义分库的逻辑
    /// </summary>
    /// <param name="shardingKey"></param>
    /// <returns>返回的是上述数据源名称中的某一个</returns>
    public override string ShardingKeyToDataSourceName(object shardingKey)
    {
        //1.对分库键求hashcode
        var hashCode = ShardingCoreHelper.GetStringHashCode(shardingKey?.ToString() ?? string.Empty);
        //2,因为分了3个库,所以对3取模,值可能为 0,1,2
        var result1 = hashCode % 3;
        //3 求绝对值
        var result2 = Math.Abs(result1);
        //4 因为上述的数据源名称00、01、02,所以借助PadLeft函数,在左侧加1个0,就组成00、01、02了
        var finalResult = result2.ToString().PadLeft(2, '0');
        return finalResult;

        //这里靠的是返回数据源的名称,返回00,则对应数据源名称为00,这个shardingkey则进入00数据源对应的DB
        //00 指的是program中定义的名称,也就是这里的_dataSources
        //至于 名称00 对应的DB的真实名称是什么,不是很关键,可以叫 ShardingDB_00 ,也可以叫 ShardingDB_AAAA



        //等价于上面的步骤
        //return Math.Abs(ShardingCoreHelper.GetStringHashCode(shardingKey?.ToString() ?? string.Empty) % 3).ToString().PadLeft(2, '0');
    }



    public override List<string> GetAllDataSourceNames()
    {
        return _dataSources;
    }

    public override bool AddDataSourceName(string dataSourceName)
    {
        if (_dataSources.Any(o => o == dataSourceName)) { return false; }
        _dataSources.Add(dataSourceName);
        return true;
    }

    public override Func<string, bool> GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
    {

        var t = ShardingKeyToDataSourceName(shardingKey);
        switch (shardingOperator)
        {
            case ShardingOperatorEnum.Equal: { return tail => tail == t; }
            default: { return tail => true; }
        }
    }

    /// <summary>
    /// 分库字段设置
    /// </summary>
    /// <param name="builder"></param>
    public override void Configure(EntityMetadataDataSourceBuilder<Order> builder)
    {
        builder.ShardingProperty(o => o.Area);
    }


}


/// <summary>
/// SysUser表的分库,也是根据Area来分库
/// </summary>
public class SysUserVirtualDataSourceRoute : AbstractShardingOperatorVirtualDataSourceRoute<SysUser, string>
{


    //数据源名称--需要和program中的名称对应
    private readonly List<string> _dataSources = ["00", "01", "02"];


    /// <summary>
    /// 自定义分库的逻辑
    /// </summary>
    /// <param name="shardingKey"></param>
    /// <returns>返回的是上述数据源名称中的某一个</returns>
    public override string ShardingKeyToDataSourceName(object shardingKey)
    {
        //1.对分库键求hashcode
        var hashCode = ShardingCoreHelper.GetStringHashCode(shardingKey?.ToString() ?? string.Empty);
        //2,因为分了3个库,所以对3取模,值可能为 0,1,2
        var result1 = hashCode % 3;
        //3 求绝对值
        var result2 = Math.Abs(result1);
        //4 因为上述的数据源名称00、01、02,所以借助PadLeft函数,在左侧加1个0,就组成00、01、02了
        var finalResult = result2.ToString().PadLeft(2, '0');
        return finalResult;

        //这里靠的是返回数据源的名称,返回00,则对应数据源名称为00,这个shardingkey则进入00数据源对应的DB
        //00 指的是program中定义的名称,也就是这里的_dataSources
        //至于 名称00 对应的DB的真实名称是什么,不是很关键,可以叫 ShardingDB_00 ,也可以叫 ShardingDB_AAAA



        //等价于上面的步骤
        //return Math.Abs(ShardingCoreHelper.GetStringHashCode(shardingKey?.ToString() ?? string.Empty) % 3).ToString().PadLeft(2, '0');
    }

    public override List<string> GetAllDataSourceNames()
    {
        return _dataSources;
    }

    public override bool AddDataSourceName(string dataSourceName)
    {
        if (_dataSources.Any(o => o == dataSourceName))
            return false;
        _dataSources.Add(dataSourceName);
        return true;
    }

    public override Func<string, bool> GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
    {

        var t = ShardingKeyToDataSourceName(shardingKey);
        switch (shardingOperator)
        {
            case ShardingOperatorEnum.Equal: return tail => tail == t;
            default:
                {
                    return tail => true;
                }
        }
    }

    public override void Configure(EntityMetadataDataSourceBuilder<SysUser> builder)
    {
        builder.ShardingProperty(o => o.Area);
    }
}
View Code

 

(2).  DB字符串配置

{
  "ConnectionStrings": {
    "SQLServerStr1": "Server=47.101.xxx.xxx;Database=ShardingDB_00;User=sa;Password=xxxxx;TrustServerCertificate=True;",
    "SQLServerStr2": "Server=47.101.xxx.xxx;Database=ShardingDB_01;User=sa;Password=xxxxx;TrustServerCertificate=True;",
    "SQLServerStr3": "Server=47.101.xxx.xxx;Database=ShardingDB_02;User=sa;Password=xxxxx;TrustServerCertificate=True;",
    "MySQLStr": ""
  },
  "AllowedHosts": "*"
}

 

(3). program中的注入代码

  注意代码:  serviceProvider.UseAutoTryCompensateTable();     //启动进行数据库和表补偿--(自行判断缺少的分片对象(包括分表或者分库),自动创建)    

代码分享: 

public class Program
{

    public static readonly ILoggerFactory efLogger = LoggerFactory.Create(builder =>
    {
        builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
    });

    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.Services.AddControllers();
        builder.Services.AddEndpointsApiExplorer();
        //注册OpenApi
        builder.Services.AddSwaggerGen(options =>
        {
            var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
            options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
        });
        var SQLServerStr1 = builder.Configuration.GetConnectionString("SQLServerStr1");
        var SQLServerStr2 = builder.Configuration.GetConnectionString("SQLServerStr2");
        var SQLServerStr3 = builder.Configuration.GetConnectionString("SQLServerStr3");

        /*
         ShardingCore相关配置
         1. AddShardingDbContext配置包含了原生的AddDbContext+AddShardingConfigure,无需单独注入原生DBContext
         2. DefaultShardingDbContext继承了原生DBContext,随意具有原生的所有功能
        */
        builder.Services.AddShardingDbContext<MyDbContext>()
               .UseRouteConfig(op =>
               {
                   op.AddShardingDataSourceRoute<OrderVirtualDataSourceRoute>();
                   op.AddShardingDataSourceRoute<SysUserVirtualDataSourceRoute>();
               })
               .UseConfig(op =>
               {

                   #region 一些配置 【暂时注释,需要什么开启什么】  
                   //{
                   //    //当查询无法匹配到对应的路由是否抛出异常 true表示抛出异常 false表示返回默认值
                   //    op.ThrowIfQueryRouteNotMatch = false;
                   //    //如果开启读写分离是否在savechange commit rollback后自动将dbcontext切换为写库 true表示是 false表示不是
                   //    op.AutoUseWriteConnectionStringAfterWriteDb = false;
                   //    //创建表如果出现错误是否忽略掉错误不进行日志出输出 true表示忽略 false表示不忽略
                   //    op.IgnoreCreateTableError = false;
                   //    //默认的系统默认迁移并发数,分库下会进行并发迁移
                   //    op.MigrationParallelCount = Environment.ProcessorCount;
                   //    //补偿表并发线程数 补偿表用来进行最后一次补偿缺少的分片表
                   //    op.CompensateTableParallelCount = Environment.ProcessorCount;
                   //    //默认最大连接数限制 如果出现跨分片查询需要开启n个链接,设置这个值会将x个链接分成每n个为一组进行同库串行执行
                   //    op.MaxQueryConnectionsLimit = Environment.ProcessorCount;
                   //    //链接模式的选择系统自动
                   //    op.ConnectionMode = ConnectionModeEnum.SYSTEM_AUTO;
                   //}
                   #endregion



                   //如何通过字符串查询创建DbContext
                   op.UseShardingQuery((conStr, builder) =>
                   {
                       builder.UseSqlServer(conStr, o => o.UseCompatibilityLevel(120)).UseLoggerFactory(efLogger);
                   });
                   //如何通过事务创建DbContext
                   op.UseShardingTransaction((connection, builder) =>
                   {
                       builder.UseSqlServer(connection, o => o.UseCompatibilityLevel(120)).UseLoggerFactory(efLogger);
                   });
                   //添加默认数据源
                   op.AddDefaultDataSource("00", SQLServerStr1);


                   //添加额外数据源--用于分库
                   op.AddExtraDataSource(sp =>
                   {
                       return new Dictionary<string, string>()
                       {
                            { "01", SQLServerStr2 },
                            { "02", SQLServerStr3 },
                       };
                   });

               })
               .AddShardingCore();



        /***************************************下面是管道相关***************************************************/

        var app = builder.Build();
        IServiceProvider serviceProvider = app.Services;
        if (app.Environment.IsDevelopment())
        {
            app.UseSwagger();
            app.UseSwaggerUI();
        }


        //启动进行数据库和表补偿--(自行判断缺少的分片对象(包括分表或者分库),自动创建)    
        serviceProvider.UseAutoTryCompensateTable();
        //初始化分库分表数据,可以放在这里,也可以定义接口初始化





        app.UseRouting();
        app.UseAuthorization();
        app.MapControllers();
        app.Run();
    }
}
View Code

 

3. 测试

(1). 启动代码,会自动生成对应的DB 和 Table。

(2). 插入测试

代码分享

  /// <summary>
  /// 插入数据
  /// </summary>
  /// <returns></returns>
  [HttpPost]
  public async Task<IActionResult> InsertData()
  {
      //数据1
      string[] areas = ["A", "B", "C"];
      List<SysUser> users = [];
      for (int i = 0; i < 10; i++)
      {
          var uer = new SysUser() { Id = i.ToString(), Name = $"MyName{i}", Area = areas[i % 3] };
          users.Add(uer);
      }
      //数据2
      List<Order> orders = [];
      var begin = new DateTime(2021, 1, 1, 3, 3, 3);
      for (int i = 0; i < 300; i++)
      {
          var order = new Order()
          {
              Id = i.ToString(),
              Payer = $"{i % 10}",
              Money = 100 + new Random().Next(100, 3000),
              OrderStatus = (OrderStatusEnum)(i % 4 + 1),
              Area = areas[i % 3],
              CreationTime = begin.AddDays(i)
          };
          orders.Add(order);
      }
      db.AddRange(orders);
      db.AddRange(users);
      int count = await db.SaveChangesAsync();
      return Json(new { status = "ok", msg = $"插入成功,条数为{count}" });
  }

结果查看

 ShardingDB_00中Order表中的Area的值都是 B

 ShardingDB_01中Order表中的Area的值都是 C

 ShardingDB_02中Order表中的Area的值都是 A

(3). 其他测试

  包括 删除、修改、join查询等等

代码分享

 /// <summary>
 /// 根据player修改
 /// </summary>
 /// <param name="playName"></param>
 /// <returns></returns>
 [HttpPost]
 public async Task<IActionResult> UpdateByPlayer(string playName)
 {
     var list = await db.Set<Order>().Where(o => o.Payer == playName).ToListAsync();
     foreach (var item in list)
     {

         item.Money = 99999;

     }
     int count = await db.SaveChangesAsync();
     return Json(new { status = "ok", msg = $"修改成功,条数为{count}" });
 }


 /// <summary>
 /// 根据player删除
 /// </summary>
 /// <param name="playName"></param>
 [HttpPost]
 public async Task<IActionResult> DelByPlayer(string playName)
 {
     var list = await db.Set<Order>().Where(o => o.Payer == playName).ToListAsync();
     foreach (var item in list)
     {
         db.Remove(item);
     }
     var count = await db.SaveChangesAsync();
     return Json(new { status = "ok", msg = $"修改成功,条数为{count}" });
 }

 /// <summary>
 /// 删除所有数据
 /// </summary>

 [HttpPost]
 public async Task<IActionResult> DelAll(string playName)
 {

     var count = await db.Set<Order>().ExecuteDeleteAsync();
     return Json(new { status = "ok", msg = $"修改成功,条数为{count}" });
 }



 /// <summary>
 /// 查询-单对象
 /// </summary>
 [HttpPost]
 public async Task<IActionResult> SearchSimple()
 {
     var list = await db.Set<Order>().Where(o => true).OrderBy(u => u.Money).ToListAsync();
     var count = list.Count();

     return Json(new { status = "ok", msg = $"查询成功,数量:{count}", data = list });
 }

 /// <summary>
 /// 查询-分库join问题
 /// 关联字段是否有索引是有区别的,这里不测试了
 /// </summary>
 [HttpPost]
 public async Task<IActionResult> SearchJoin()
 {
     var begin = new DateTime(2021, 3, 2);
     var end = new DateTime(2021, 4, 3);
     var sql1 = from user in db.Set<SysUser>().Where(o => o.Id == "1" || o.Id == "6")
                join order in db.Set<Order>().Where(o => o.CreationTime >= begin && o.CreationTime <= end)
                    on user.Id equals order.Payer
                select new
                {
                    user.Id,
                    user.Name,
                    user.Area,
                    OrderId = order.Id,
                    order.Payer,
                    order.CreationTime
                };
     var list = await sql1.ToListAsync();

     return Json(new { status = "ok", msg = $"查询成功,数量:{list.Count}", data = list });
 }
View Code

 

 

二. 分库且分表-实操

1. 目标

    有三个DB,分别是ShardingDB_00、ShardingDB_01、ShardingDB_02,每个DB中都有6张表:SysUser_00、SysUser_01、SysUser_02、Order_2022、Order_2023、Order_2024。

    (1). Order表中的数据,先按照用户区域字段Area来分库,在每个库的基础上,再按照时间字段 creationTime 来分表。

    (2). SysUser表中的数据,先按照用户区域字段Area来分库,在每个库的基础上,再按照ID字段 ID 来分表。

 PS:Area字段是string类型,所以需要自己设计分库算法, 我们这里的算法为:先对字段求hashcode,然后对 3 取模,得到 0、1、2, 正好存放到xx_00、xx_01、xx_02 数据库中。

 

2. 实操步骤

(这里只分享核心代码,表实体代码、EFCore上下文代码 不再重复)

(1) 分库路由

   同上 单独的只分库的路由相同。

 

(2) 分表路由

   Order表---根据CreationTime分表


/// <summary>
/// order表根据creationTime时间来分表,以年为单位
/// </summary>
public class OrderVirtualTableRoute : AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute<Order>
{
    /// <summary>
    /// 自动建表
    /// (目前没发现作用,即使为false也会自动建表)(无效!!)
    /// </summary>
    /// <returns></returns>
    public override bool AutoCreateTableByTime()
    {
        return true;
    }

    /// <summary>
    /// 配置分表键
    /// </summary>
    /// <param name="builder"></param>
    public override void Configure(EntityMetadataTableBuilder<Order> builder)
    {
        builder.ShardingProperty(u => u.CreationTime);
    }
    /// <summary>
    /// 配置开始时间
    /// </summary>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public override DateTime GetBeginTime()
    {
        return new DateTime(2022, 1, 1);
    }
}

  SysUser表--根据ID分表

/// <summary>
/// SysUser表根据string类型Id进行分表
/// </summary>
public class SysUserVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualTableRoute<SysUser>
{
    /// <summary>
    /// base中第1个参数:表示分表名称后面的长度
    ///  2表示分表后的格式:xxx_01
    ///  3表示分表后的格式:xxx_001
    ///  4表示分表后的格式:xxx_0001
    /// base中的第2个参数:表示分表取模的除数 和 自动建表的个数
    ///  如下3,表示对3取模, 自动建表的时候会建三张表 xxx_00 xxx_01 xxx_02
    ///  注:就算不使用框架的自动建表,手动建表也需要按照这个格式
    /// 
    /// </summary>
    public SysUserVirtualTableRoute() : base(2, 3)
    {
    }

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

 

(3) DB字符串

   同上,只分库的字符串

 

(4). program中的注入代码

  注意代码:  serviceProvider.UseAutoTryCompensateTable();     //启动进行数据库和表补偿--(自行判断缺少的分片对象(包括分表或者分库),自动创建)    

代码分享:  

public class Program
{
    public static readonly ILoggerFactory efLogger = LoggerFactory.Create(builder =>
    {
        builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
    });

    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.Services.AddControllers();
        builder.Services.AddEndpointsApiExplorer();
        //注册OpenApi
        builder.Services.AddSwaggerGen(options =>
        {
            var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
            options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
        });
        var SQLServerStr1 = builder.Configuration.GetConnectionString("SQLServerStr1");
        var SQLServerStr2 = builder.Configuration.GetConnectionString("SQLServerStr2");
        var SQLServerStr3 = builder.Configuration.GetConnectionString("SQLServerStr3");

        /*
         ShardingCore相关配置
         1. AddShardingDbContext配置包含了原生的AddDbContext+AddShardingConfigure,无需单独注入原生DBContext
         2. DefaultShardingDbContext继承了原生DBContext,随意具有原生的所有功能
        */
        builder.Services.AddShardingDbContext<MyDbContext>()
               .UseRouteConfig(o =>
               {
                   //添加分库路由
                   o.AddShardingDataSourceRoute<OrderVirtualDataSourceRoute>();
                   o.AddShardingDataSourceRoute<SysUserVirtualDataSourceRoute>();
                   //添加分表路由
                   o.AddShardingTableRoute<SysUserVirtualTableRoute>();
                   o.AddShardingTableRoute<OrderVirtualTableRoute>();

               })
               .UseConfig(op =>
               {

                   #region 一些配置 【暂时注释,需要什么开启什么】  
                   //{
                   //    //当查询无法匹配到对应的路由是否抛出异常 true表示抛出异常 false表示返回默认值
                   //    op.ThrowIfQueryRouteNotMatch = false;
                   //    //如果开启读写分离是否在savechange commit rollback后自动将dbcontext切换为写库 true表示是 false表示不是
                   //    op.AutoUseWriteConnectionStringAfterWriteDb = false;
                   //    //创建表如果出现错误是否忽略掉错误不进行日志出输出 true表示忽略 false表示不忽略
                   //    op.IgnoreCreateTableError = false;
                   //    //默认的系统默认迁移并发数,分库下会进行并发迁移
                   //    op.MigrationParallelCount = Environment.ProcessorCount;
                   //    //补偿表并发线程数 补偿表用来进行最后一次补偿缺少的分片表
                   //    op.CompensateTableParallelCount = Environment.ProcessorCount;
                   //    //默认最大连接数限制 如果出现跨分片查询需要开启n个链接,设置这个值会将x个链接分成每n个为一组进行同库串行执行
                   //    op.MaxQueryConnectionsLimit = Environment.ProcessorCount;
                   //    //链接模式的选择系统自动
                   //    op.ConnectionMode = ConnectionModeEnum.SYSTEM_AUTO;
                   //}
                   #endregion


                   //如何通过字符串查询创建DbContext
                   op.UseShardingQuery((conStr, builder) =>
                   {
                       builder.UseSqlServer(conStr, o => o.UseCompatibilityLevel(120)).UseLoggerFactory(efLogger);
                   });
                   //如何通过事务创建DbContext
                   op.UseShardingTransaction((connection, builder) =>
                   {
                       builder.UseSqlServer(connection, o => o.UseCompatibilityLevel(120)).UseLoggerFactory(efLogger);
                   });
                   //添加默认数据源
                   op.AddDefaultDataSource("00", SQLServerStr1);


                   //添加额外数据源--用于分库
                   op.AddExtraDataSource(sp =>
                   {
                       return new Dictionary<string, string>()
                       {
                            { "01", SQLServerStr2 },
                            { "02", SQLServerStr3 },
                       };
                   });

               })
               .AddShardingCore();



        /***************************************下面是管道相关***************************************************/

        var app = builder.Build();
        IServiceProvider serviceProvider = app.Services;
        if (app.Environment.IsDevelopment())
        {
            app.UseSwagger();
            app.UseSwaggerUI();
        }


        //启动进行数据库和表补偿--(自行判断缺少的分片对象(包括分表或者分库),自动创建)    
        serviceProvider.UseAutoTryCompensateTable();
        //初始化分库分表数据,可以放在这里,也可以定义接口初始化





        app.UseRouting();
        app.UseAuthorization();
        app.MapControllers();
        app.Run();
    }
}
View Code

 

 

3. 测试

(1). 启动代码,会自动生成对应的DB 和 Table

(2). 插入测试

代码分享

  /// <summary>
  /// 插入数据
  /// </summary>
  /// <returns></returns>
  [HttpPost]
  public async Task<IActionResult> InsertData()
  {
      //数据1
      string[] areas = ["A", "B", "C"];
      List<SysUser> users = [];
      for (int i = 0; i < 100; i++)
      {
          var user = new SysUser()
          {
              Id = i.ToString(),
              Name = $"MyName{i}",
              Area = areas[new Random().Next(0, 3)]
          };
          users.Add(user);
      }
      //数据2
      List<Order> orders = [];
      var begin = new DateTime(2022, 1, 1);
      for (int i = 0; i < 300; i++)
      {
          var sysUser = users[i % 100];
          var order = new Order()
          {
              Id = i.ToString(),
              Payer = sysUser.Id,
              Money = 100 + new Random().Next(100, 3000),
              OrderStatus = (OrderStatusEnum)(i % 4 + 1),
              Area = sysUser.Area,
          };
          //让时间平均分配在 2022、2023、2024年度
          if (i < 100)
          {
              order.CreationTime = begin.AddDays(i);
          }
          if (i >= 100 && i < 200)
          {
              order.CreationTime = begin.AddYears(1).AddDays(i);
          }
          if (i >= 200 && i < 300)
          {
              order.CreationTime = begin.AddYears(2).AddDays(i);
          }

          orders.Add(order);
      }
      db.AddRange(orders);
      db.AddRange(users);
      int count = await db.SaveChangesAsync();
      return Json(new { status = "ok", msg = $"插入成功,条数为{count}" });
  }

结果分析:

ShardingDB_00中Order表中的Area的值都是 B,每张表按照createTime分表, SysUser表中的Area的值都是 B,每张表按照ID分表

 ShardingDB_01中Order表中的Area的值都是 C,每张表按照createTime分表, SysUser表中的Area的值都是 C,每张表按照ID分表

 ShardingDB_02中Order表中的Area的值都是 A,每张表按照createTime分表, SysUser表中的Area的值都是 A,每张表按照ID分表

 

(3) 其他代码 

  /// <summary>
  /// 删除所有数据
  /// </summary>

  [HttpPost]
  public async Task<IActionResult> DelAll()
  {

      var count1 = await db.Set<Order>().ExecuteDeleteAsync();
      var count2 = await db.Set<SysUser>().ExecuteDeleteAsync();
      return Json(new { status = "ok", msg = $"修改成功,条数为{count1 + count2}" });
  }


 


  /// <summary>
  /// 根据player修改
  /// </summary>
  /// <param name="playName"></param>
  /// <returns></returns>
  [HttpPost]
  public async Task<IActionResult> UpdateByPlayer(string playName)
  {
      var list = await db.Set<Order>().Where(o => o.Payer == playName).ToListAsync();
      foreach (var item in list)
      {

          item.Money = 99999;

      }
      int count = await db.SaveChangesAsync();
      return Json(new { status = "ok", msg = $"修改成功,条数为{count}" });
  }


  /// <summary>
  /// 根据player删除
  /// </summary>
  /// <param name="playName"></param>
  [HttpPost]
  public async Task<IActionResult> DelByPlayer(string playName)
  {
      var list = await db.Set<Order>().Where(o => o.Payer == playName).ToListAsync();
      foreach (var item in list)
      {
          db.Remove(item);
      }
      var count = await db.SaveChangesAsync();
      return Json(new { status = "ok", msg = $"修改成功,条数为{count}" });
  }


  /// <summary>
  /// 查询-单对象
  /// </summary>
  /// <param name="orderId">订单主键</param>
  /// <param name="userId">用户主键</param>
  /// <returns></returns>
  [HttpPost]
  public async Task<IActionResult> SearchSimple(string orderId, string userId)
  {
      var data1 = await db.Set<Order>().Where(o => o.Id == orderId).FirstOrDefaultAsync();
      var data2 = await db.Set<SysUser>().Where(o => o.Id == userId).FirstOrDefaultAsync();

      return Json(new { status = "ok", msg = $"查询成功", data = new { data1, data2 } });
  }


  /// <summary>
  /// 查询-分库join问题
  /// 关联字段是否有索引是有区别的,这里不测试了
  /// </summary>
  [HttpPost]
  public async Task<IActionResult> SearchJoin()
  {
      var begin = new DateTime(2021, 3, 2);
      var end = new DateTime(2025, 4, 3);
      var sql1 = from user in db.Set<SysUser>().Where(o => o.Id == "1" || o.Id == "6")
                 join order in db.Set<Order>().Where(o => o.CreationTime >= begin && o.CreationTime <= end)
                     on user.Id equals order.Payer
                 select new
                 {
                     user.Id,
                     user.Name,
                     user.Area,
                     OrderId = order.Id,
                     order.Payer,
                     order.CreationTime
                 };
      var list = await sql1.ToListAsync();

      return Json(new { status = "ok", msg = $"查询成功,数量:{list.Count}", data = list });
  }
View Code

 

 

 

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2024-11-17 18:54  Yaopengfei  阅读(21)  评论(3编辑  收藏  举报