diff --git a/ShardingCore.sln b/ShardingCore.sln index aca63651..2a76d07f 100644 --- a/ShardingCore.sln +++ b/ShardingCore.sln @@ -39,7 +39,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShardingCore.Test50.MySql", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.AutoByDate.SqlServer", "samples\Samples.AutoByDate.SqlServer\Samples.AutoByDate.SqlServer.csproj", "{C34FCF48-1A98-4268-BFEE-6C9BFC7FD539}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.MySql", "samples\Sample.MySql\Sample.MySql.csproj", "{90675788-D5C3-415A-9C18-FF159A75B4D5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.MySql", "samples\Sample.MySql\Sample.MySql.csproj", "{90675788-D5C3-415A-9C18-FF159A75B4D5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.SqlServer3x", "samples\Sample.SqlServer3x\Sample.SqlServer3x.csproj", "{447D5357-F095-45DE-9DA5-2D9997237366}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCoreTestSqlServer3x", "test\ShardingCoreTestSqlServer3x\ShardingCoreTestSqlServer3x.csproj", "{1CE858B8-56D8-4009-BF46-EE0F79F259D1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -103,6 +107,14 @@ Global {90675788-D5C3-415A-9C18-FF159A75B4D5}.Debug|Any CPU.Build.0 = Debug|Any CPU {90675788-D5C3-415A-9C18-FF159A75B4D5}.Release|Any CPU.ActiveCfg = Release|Any CPU {90675788-D5C3-415A-9C18-FF159A75B4D5}.Release|Any CPU.Build.0 = Release|Any CPU + {447D5357-F095-45DE-9DA5-2D9997237366}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {447D5357-F095-45DE-9DA5-2D9997237366}.Debug|Any CPU.Build.0 = Debug|Any CPU + {447D5357-F095-45DE-9DA5-2D9997237366}.Release|Any CPU.ActiveCfg = Release|Any CPU + {447D5357-F095-45DE-9DA5-2D9997237366}.Release|Any CPU.Build.0 = Release|Any CPU + {1CE858B8-56D8-4009-BF46-EE0F79F259D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1CE858B8-56D8-4009-BF46-EE0F79F259D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1CE858B8-56D8-4009-BF46-EE0F79F259D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1CE858B8-56D8-4009-BF46-EE0F79F259D1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -122,6 +134,8 @@ Global {C8FAB96F-F13E-4094-883C-2D38D39EE4A3} = {CC2C88C0-65F2-445D-BE78-973B840FE281} {C34FCF48-1A98-4268-BFEE-6C9BFC7FD539} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73} {90675788-D5C3-415A-9C18-FF159A75B4D5} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73} + {447D5357-F095-45DE-9DA5-2D9997237366} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73} + {1CE858B8-56D8-4009-BF46-EE0F79F259D1} = {CC2C88C0-65F2-445D-BE78-973B840FE281} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8C07A667-E8B4-43C7-8053-721584BAD291} diff --git a/samples/Sample.SqlServer3x/Controllers/WeatherForecastController.cs b/samples/Sample.SqlServer3x/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..bc176cb5 --- /dev/null +++ b/samples/Sample.SqlServer3x/Controllers/WeatherForecastController.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Sample.SqlServer3x.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public IEnumerable Get() + { + var rng = new Random(); + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = rng.Next(-20, 55), + Summary = Summaries[rng.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} diff --git a/samples/Sample.SqlServer3x/DefaultDbContext.cs b/samples/Sample.SqlServer3x/DefaultDbContext.cs new file mode 100644 index 00000000..2522b9c4 --- /dev/null +++ b/samples/Sample.SqlServer3x/DefaultDbContext.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore; +using Sample.SqlServer3x.Domain.Maps; +using ShardingCore.DbContexts.ShardingDbContexts; + +namespace Sample.SqlServer3x +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/3/31 15:28:11 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class DefaultDbContext : DbContext, IShardingTableDbContext + { + public DefaultDbContext(DbContextOptions options) : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.ApplyConfiguration(new SysUserModMap()); + } + + public string ModelChangeKey { get; set; } + } +} diff --git a/samples/Sample.SqlServer3x/Domain/Entities/SysUserMod.cs b/samples/Sample.SqlServer3x/Domain/Entities/SysUserMod.cs new file mode 100644 index 00000000..fbd0b423 --- /dev/null +++ b/samples/Sample.SqlServer3x/Domain/Entities/SysUserMod.cs @@ -0,0 +1,29 @@ +using ShardingCore.Core; + +namespace Sample.SqlServer3x.Domain.Entities +{ +/* +* @Author: xjm +* @Description: +* @Date: Thursday, 14 January 2021 15:36:43 +* @Email: 326308290@qq.com +*/ + public class SysUserMod:IShardingTable + { + /// + /// 用户Id用于分表 + /// + [ShardingTableKey] + public string Id { get; set; } + /// + /// 用户名称 + /// + public string Name { get; set; } + /// + /// 用户姓名 + /// + public int Age { get; set; } + public int AgeGroup { get; set; } + + } +} \ No newline at end of file diff --git a/samples/Sample.SqlServer3x/Domain/Maps/SysUserModMap.cs b/samples/Sample.SqlServer3x/Domain/Maps/SysUserModMap.cs new file mode 100644 index 00000000..b8435ab3 --- /dev/null +++ b/samples/Sample.SqlServer3x/Domain/Maps/SysUserModMap.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Sample.SqlServer3x.Domain.Entities; + +namespace Sample.SqlServer3x.Domain.Maps +{ +/* +* @Author: xjm +* @Description: +* @Date: Thursday, 14 January 2021 15:37:33 +* @Email: 326308290@qq.com +*/ + public class SysUserModMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.Id).IsRequired().HasMaxLength(128); + builder.Property(o => o.Name).HasMaxLength(128); + builder.ToTable(nameof(SysUserMod)); + } + } +} \ No newline at end of file diff --git a/samples/Sample.SqlServer3x/Program.cs b/samples/Sample.SqlServer3x/Program.cs new file mode 100644 index 00000000..b27db232 --- /dev/null +++ b/samples/Sample.SqlServer3x/Program.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Sample.SqlServer3x +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/samples/Sample.SqlServer3x/Properties/launchSettings.json b/samples/Sample.SqlServer3x/Properties/launchSettings.json new file mode 100644 index 00000000..f370aef4 --- /dev/null +++ b/samples/Sample.SqlServer3x/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55565", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Sample.SqlServer3x": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/Sample.SqlServer3x/Sample.SqlServer3x.csproj b/samples/Sample.SqlServer3x/Sample.SqlServer3x.csproj new file mode 100644 index 00000000..70e926fa --- /dev/null +++ b/samples/Sample.SqlServer3x/Sample.SqlServer3x.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.1 + + + + + + + + diff --git a/samples/Sample.SqlServer3x/Shardings/SysUserModVirtualTableRoute.cs b/samples/Sample.SqlServer3x/Shardings/SysUserModVirtualTableRoute.cs new file mode 100644 index 00000000..39588207 --- /dev/null +++ b/samples/Sample.SqlServer3x/Shardings/SysUserModVirtualTableRoute.cs @@ -0,0 +1,19 @@ +using Sample.SqlServer3x.Domain.Entities; +using ShardingCore.VirtualRoutes.Mods; + +namespace Sample.SqlServer3x.Shardings +{ +/* +* @Author: xjm +* @Description: +* @Date: Thursday, 14 January 2021 15:39:27 +* @Email: 326308290@qq.com +*/ + public class SysUserModVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualTableRoute + { + public SysUserModVirtualTableRoute() : base(2,3) + { + } + + } +} \ No newline at end of file diff --git a/samples/Sample.SqlServer3x/Startup.cs b/samples/Sample.SqlServer3x/Startup.cs new file mode 100644 index 00000000..ce8ce2bb --- /dev/null +++ b/samples/Sample.SqlServer3x/Startup.cs @@ -0,0 +1,105 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Sample.SqlServer3x.Domain.Entities; +using Sample.SqlServer3x.Shardings; +using ShardingCore; +using ShardingCore.DbContexts.VirtualDbContexts; +using ShardingCore.Extensions; +using ShardingCore.SqlServer; + +namespace Sample.SqlServer3x +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + services.AddShardingSqlServer(o => + { + o.EnsureCreatedWithOutShardingTable = true; + o.CreateShardingTableOnStart = true; + o.AddShardingDbContextWithShardingTable("conn1", "Data Source=localhost;Initial Catalog=ShardingCoreDB3x;Integrated Security=True", dbConfig => + { + dbConfig.AddShardingTableRoute(); + }); + //o.AddDataSourceVirtualRoute<>(); + + }); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + var shardingBootstrapper = app.ApplicationServices.GetService(); + shardingBootstrapper.Start(); + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + + InitData(app).GetAwaiter().GetResult(); + } + + /// + /// + /// + /// + /// + private async Task InitData(IApplicationBuilder app) + { + var serviceProvider = app.ApplicationServices; + using (var scope = serviceProvider.CreateScope()) + { + var virtualDbContext = scope.ServiceProvider.GetService(); + if (!await virtualDbContext.Set().ShardingAnyAsync(o => true)) + { + var ids = Enumerable.Range(1, 1000); + var userMods = new List(); + var beginTime = new DateTime(2020, 1, 1); + var endTime = new DateTime(2021, 12, 1); + foreach (var id in ids) + { + userMods.Add(new SysUserMod() + { + Id = id.ToString(), + Age = id, + Name = $"name_{id}", + AgeGroup = Math.Abs(id % 10) + }); + + } + + await virtualDbContext.InsertRangeAsync(userMods); + + await virtualDbContext.SaveChangesAsync(); + } + } + } +} +} diff --git a/samples/Sample.SqlServer3x/WeatherForecast.cs b/samples/Sample.SqlServer3x/WeatherForecast.cs new file mode 100644 index 00000000..5fd7fd7c --- /dev/null +++ b/samples/Sample.SqlServer3x/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace Sample.SqlServer3x +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} diff --git a/samples/Sample.SqlServer3x/appsettings.Development.json b/samples/Sample.SqlServer3x/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/samples/Sample.SqlServer3x/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/samples/Sample.SqlServer3x/appsettings.json b/samples/Sample.SqlServer3x/appsettings.json new file mode 100644 index 00000000..d9d9a9bf --- /dev/null +++ b/samples/Sample.SqlServer3x/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/test/ShardingCore.Test50/DefaultDbContext.cs b/test/ShardingCore.Test50/DefaultDbContext.cs new file mode 100644 index 00000000..249862ef --- /dev/null +++ b/test/ShardingCore.Test50/DefaultDbContext.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using ShardingCore.DbContexts.ShardingDbContexts; +using ShardingCore.Test50.Domain.Maps; + +namespace ShardingCore.Test50 +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/3/31 15:28:11 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class DefaultDbContext : DbContext, IShardingTableDbContext + { + public DefaultDbContext(DbContextOptions options) : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.ApplyConfiguration(new SysUserModMap()); + modelBuilder.ApplyConfiguration(new SysUserSalaryMap()); + } + + public string ModelChangeKey { get; set; } + } +} diff --git a/test/ShardingCore.Test50/Startup.cs b/test/ShardingCore.Test50/Startup.cs index 1b1f4f0d..279868cc 100644 --- a/test/ShardingCore.Test50/Startup.cs +++ b/test/ShardingCore.Test50/Startup.cs @@ -44,7 +44,20 @@ namespace ShardingCore.Test50 // ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext) // ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services) public void ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext) - { + { + + services.AddShardingSqlServer(o => + { + o.EnsureCreatedWithOutShardingTable = true; + o.CreateShardingTableOnStart = true; + o.AddShardingDbContextWithShardingTable("conn1", hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"], dbConfig => + { + dbConfig.AddShardingTableRoute(); + dbConfig.AddShardingTableRoute(); + }); + //o.AddDataSourceVirtualRoute<>(); + + }); //services.AddShardingSqlServer(o => //{ // o.ConnectionString = hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"]; diff --git a/test/ShardingCoreTestSqlServer3x/Configs/DbConfig.json b/test/ShardingCoreTestSqlServer3x/Configs/DbConfig.json new file mode 100644 index 00000000..56dbea2d --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/Configs/DbConfig.json @@ -0,0 +1,5 @@ +{ + "SqlServer": { + "ConnectionString": "Data Source=localhost;Initial Catalog=ShardingCoreDB3x;Integrated Security=True" + } +} \ No newline at end of file diff --git a/test/ShardingCoreTestSqlServer3x/DefaultDbContext.cs b/test/ShardingCoreTestSqlServer3x/DefaultDbContext.cs new file mode 100644 index 00000000..5ac00eef --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/DefaultDbContext.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using ShardingCore.DbContexts.ShardingDbContexts; +using ShardingCoreTestSqlServer3x.Domain.Maps; + +namespace ShardingCoreTestSqlServer3x +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/3/31 15:28:11 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class DefaultDbContext : DbContext, IShardingTableDbContext + { + public DefaultDbContext(DbContextOptions options) : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.ApplyConfiguration(new SysUserModMap()); + modelBuilder.ApplyConfiguration(new SysUserSalaryMap()); + } + + public string ModelChangeKey { get; set; } + } +} diff --git a/test/ShardingCoreTestSqlServer3x/Domain/Entities/SysUserMod.cs b/test/ShardingCoreTestSqlServer3x/Domain/Entities/SysUserMod.cs new file mode 100644 index 00000000..7ac1c075 --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/Domain/Entities/SysUserMod.cs @@ -0,0 +1,29 @@ +using ShardingCore.Core; + +namespace ShardingCoreTestSqlServer3x.Domain.Entities +{ +/* +* @Author: xjm +* @Description: +* @Date: Thursday, 14 January 2021 15:36:43 +* @Email: 326308290@qq.com +*/ + public class SysUserMod:IShardingTable + { + /// + /// 用户Id用于分表 + /// + [ShardingTableKey] + public string Id { get; set; } + /// + /// 用户名称 + /// + public string Name { get; set; } + /// + /// 用户姓名 + /// + public int Age { get; set; } + public int AgeGroup { get; set; } + + } +} \ No newline at end of file diff --git a/test/ShardingCoreTestSqlServer3x/Domain/Entities/SysUserSalary.cs b/test/ShardingCoreTestSqlServer3x/Domain/Entities/SysUserSalary.cs new file mode 100644 index 00000000..62af6cfc --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/Domain/Entities/SysUserSalary.cs @@ -0,0 +1,42 @@ +using ShardingCore.Core; + +namespace ShardingCoreTestSqlServer3x.Domain.Entities +{ +/* +* @Author: xjm +* @Description: +* @Date: Monday, 01 February 2021 15:43:22 +* @Email: 326308290@qq.com +*/ + public class SysUserSalary:IShardingTable + { + public string Id { get; set; } + public string UserId { get; set; } + /// + /// 每月的金额 + /// + [ShardingTableKey] + public int DateOfMonth { get; set; } + /// + /// 工资 + /// + public int Salary { get; set; } + /// + /// 工资 + /// + public long SalaryLong { get; set; } + + /// + /// 工资 + /// + public decimal SalaryDecimal { get; set; } + /// + /// 工资 + /// + public double SalaryDouble { get; set; } + /// + /// 工资 + /// + public float SalaryFloat { get; set; } + } +} \ No newline at end of file diff --git a/test/ShardingCoreTestSqlServer3x/Domain/Maps/SysUserModMap.cs b/test/ShardingCoreTestSqlServer3x/Domain/Maps/SysUserModMap.cs new file mode 100644 index 00000000..ce0fb495 --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/Domain/Maps/SysUserModMap.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCoreTestSqlServer3x.Domain.Entities; + +namespace ShardingCoreTestSqlServer3x.Domain.Maps +{ +/* +* @Author: xjm +* @Description: +* @Date: Thursday, 14 January 2021 15:37:33 +* @Email: 326308290@qq.com +*/ + public class SysUserModMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.Id).IsRequired().HasMaxLength(128); + builder.Property(o => o.Name).HasMaxLength(128); + builder.ToTable(nameof(SysUserMod)); + } + } +} \ No newline at end of file diff --git a/test/ShardingCoreTestSqlServer3x/Domain/Maps/SysUserSalaryMap.cs b/test/ShardingCoreTestSqlServer3x/Domain/Maps/SysUserSalaryMap.cs new file mode 100644 index 00000000..2e6a0673 --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/Domain/Maps/SysUserSalaryMap.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCoreTestSqlServer3x.Domain.Entities; + +namespace ShardingCoreTestSqlServer3x.Domain.Maps +{ +/* +* @Author: xjm +* @Description: +* @Date: Monday, 01 February 2021 15:42:35 +* @Email: 326308290@qq.com +*/ + public class SysUserSalaryMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.Id).IsRequired().HasMaxLength(128); + builder.Property(o => o.UserId).IsRequired().HasMaxLength(128); + builder.ToTable(nameof(SysUserSalary)); + } + } +} \ No newline at end of file diff --git a/test/ShardingCoreTestSqlServer3x/ShardingCoreTestSqlServer3x.csproj b/test/ShardingCoreTestSqlServer3x/ShardingCoreTestSqlServer3x.csproj new file mode 100644 index 00000000..e0a143c5 --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/ShardingCoreTestSqlServer3x.csproj @@ -0,0 +1,29 @@ + + + + net5.0 + 9.0 + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + true + PreserveNewest + PreserveNewest + + + + + + + \ No newline at end of file diff --git a/test/ShardingCoreTestSqlServer3x/ShardingTest.cs b/test/ShardingCoreTestSqlServer3x/ShardingTest.cs new file mode 100644 index 00000000..17fea00d --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/ShardingTest.cs @@ -0,0 +1,352 @@ +using System.Linq; +using System.Threading.Tasks; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RoutingRuleEngine; +using ShardingCore.DbContexts.VirtualDbContexts; +using ShardingCore.Extensions; +using ShardingCoreTestSqlServer3x.Domain.Entities; +using Xunit; + +namespace ShardingCoreTestSqlServer3x +{ +/* +* @Author: xjm +* @Description: +* @Date: Friday, 15 January 2021 17:22:10 +* @Email: 326308290@qq.com +*/ + public class ShardingTest + { + private readonly IVirtualDbContext _virtualDbContext; + private readonly IRoutingRuleEngineFactory _routingRuleEngineFactory; + + public ShardingTest(IVirtualDbContext virtualDbContext,IRoutingRuleEngineFactory routingRuleEngineFactory) + { + _virtualDbContext = virtualDbContext; + _routingRuleEngineFactory = routingRuleEngineFactory; + } + + //[Fact] + //public async Task Route_TEST() + //{ + // var queryable1 = _virtualDbContext.Set().Where(o=>o.Id=="339"); + // var routeResults1 = _routingRuleEngineFactory.Route(queryable1); + // Assert.Equal(1,routeResults1.Count()); + // Assert.Equal(1,routeResults1.FirstOrDefault().ReplaceTables.Count()); + // Assert.Equal("0",routeResults1.FirstOrDefault().ReplaceTables.FirstOrDefault().Tail); + // Assert.Equal(nameof(SysUserMod),routeResults1.FirstOrDefault().ReplaceTables.FirstOrDefault().OriginalName); + // var ids = new[] {"339", "124","142"}; + // var queryable2= _virtualDbContext.Set().Where(o=>ids.Contains(o.Id)); + // var routeResult2s = _routingRuleEngineFactory.Route(queryable2); + // Assert.Equal(2,routeResult2s.Count()); + // Assert.Equal(1,routeResult2s.FirstOrDefault().ReplaceTables.Count()); + // Assert.Equal(2,routeResult2s.SelectMany(o=>o.ReplaceTables).Count()); + // Assert.Equal(true,routeResult2s.SelectMany(o=>o.ReplaceTables).All(o=>new[]{"0","1"}.Contains(o.Tail))); + //} + [Fact] + public async Task ToList_All_Test() + { + var mods = await _virtualDbContext.Set().ToShardingListAsync(); + Assert.Equal(1000, mods.Count); + } + + [Fact] + public async Task ToList_Join_Test() + { + var list = await (from u in _virtualDbContext.Set() + join salary in _virtualDbContext.Set() + on u.Id equals salary.UserId + select new + { + Salary = salary.Salary, + DateOfMonth = salary.DateOfMonth, + Name = u.Name + }).ToShardingListAsync(); + Assert.Equal(24000, list.Count()); + Assert.Equal(24, list.Count(o => o.Name == "name_200")); + + + var queryable = (from u in _virtualDbContext.Set().Where(o => o.Id == "300") + join salary in _virtualDbContext.Set() + on u.Id equals salary.UserId + select new + { + Salary = salary.Salary, + DateOfMonth = salary.DateOfMonth, + Name = u.Name + }); + var list1 = await queryable.ToShardingListAsync(); + Assert.Equal(24, list1.Count()); + Assert.DoesNotContain(list1, o => o.Name != "name_300"); + } + + [Fact] + public async Task ToList_OrderBy_Asc_Desc_Test() + { + var modascs = await _virtualDbContext.Set().OrderBy(o => o.Age).ToShardingListAsync(); + Assert.Equal(1000, modascs.Count); + var i = 1; + foreach (var age in modascs) + { + Assert.Equal(i, age.Age); + i++; + } + + var moddescs = await _virtualDbContext.Set().OrderByDescending(o => o.Age).ToShardingListAsync(); + Assert.Equal(1000, moddescs.Count); + var j = 1000; + foreach (var age in moddescs) + { + Assert.Equal(j, age.Age); + j--; + } + } + + [Fact] + public async Task ToList_Id_In_Test() + { + var ids = new[] {"1", "2", "3", "4"}; + var sysUserMods = await _virtualDbContext.Set().Where(o => ids.Contains(o.Id)).ToShardingListAsync(); + foreach (var id in ids) + { + Assert.Contains(sysUserMods, o => o.Id == id); + } + + Assert.DoesNotContain(sysUserMods, o => o.Age > 4); + } + + [Fact] + public async Task ToList_Id_Eq_Test() + { + var mods = await _virtualDbContext.Set().Where(o => o.Id == "3").ToShardingListAsync(); + Assert.Single(mods); + Assert.Equal("3", mods[0].Id); + } + + [Fact] + public async Task ToList_Id_Not_Eq_Test() + { + var mods = await _virtualDbContext.Set().Where(o => o.Id != "3").ToShardingListAsync(); + Assert.Equal(999, mods.Count); + Assert.DoesNotContain(mods, o => o.Id == "3"); + } + + [Fact] + public async Task ToList_Id_Not_Eq_Skip_Test() + { + var mods = await _virtualDbContext.Set().Where(o => o.Id != "3").OrderBy(o => o.Age).Skip(2).ToShardingListAsync(); + Assert.Equal(997, mods.Count); + Assert.DoesNotContain(mods, o => o.Id == "3"); + Assert.Equal(4, mods[0].Age); + Assert.Equal(5, mods[1].Age); + + var modsDesc = await _virtualDbContext.Set().Where(o => o.Id != "3").OrderByDescending(o => o.Age).Skip(13).ToShardingListAsync(); + Assert.Equal(986, modsDesc.Count); + Assert.DoesNotContain(mods, o => o.Id == "3"); + Assert.Equal(987, modsDesc[0].Age); + Assert.Equal(986, modsDesc[1].Age); + } + + [Fact] + public async Task ToList_Name_Eq_Test() + { + var mods = await _virtualDbContext.Set().Where(o => o.Name == "name_3").ToShardingListAsync(); + Assert.Single(mods); + Assert.Equal("3", mods[0].Id); + } + + [Fact] + public async Task ToList_Id_Eq_Not_In_Db_Test() + { + var mods = await _virtualDbContext.Set().Where(o => o.Id == "1001").ToShardingListAsync(); + Assert.Empty(mods); + } + + [Fact] + public async Task ToList_Name_Eq_Not_In_Db_Test() + { + var mods = await _virtualDbContext.Set().Where(o => o.Name == "name_1001").ToShardingListAsync(); + Assert.Empty(mods); + } + + [Fact] + public async Task FirstOrDefault_Order_By_Id_Test() + { + var sysUserModAge = await _virtualDbContext.Set().OrderBy(o => o.Age).ShardingFirstOrDefaultAsync(); + Assert.True(sysUserModAge != null && sysUserModAge.Id == "1"); + var sysUserModAgeDesc = await _virtualDbContext.Set().OrderByDescending(o => o.Age).ShardingFirstOrDefaultAsync(); + Assert.True(sysUserModAgeDesc != null && sysUserModAgeDesc.Id == "1000"); + var sysUserMod = await _virtualDbContext.Set().OrderBy(o => o.Id).ShardingFirstOrDefaultAsync(); + Assert.True(sysUserMod != null && sysUserMod.Id == "1"); + + var sysUserModDesc = await _virtualDbContext.Set().OrderByDescending(o => o.Id).ShardingFirstOrDefaultAsync(); + Assert.True(sysUserModDesc != null && sysUserModDesc.Id == "999"); + } + + [Fact] + public async Task FirstOrDefault2() + { + var sysUserMod = await _virtualDbContext.Set().Where(o => o.Id == "1").ShardingFirstOrDefaultAsync(); + Assert.NotNull(sysUserMod); + Assert.True(sysUserMod.Id == "1"); + } + + [Fact] + public async Task FirstOrDefault3() + { + var sysUserMod = await _virtualDbContext.Set().Where(o => o.Name == "name_2").ShardingFirstOrDefaultAsync(); + Assert.NotNull(sysUserMod); + Assert.Equal("2", sysUserMod.Id); + } + + [Fact] + public async Task FirstOrDefault4() + { + var sysUserMod = await _virtualDbContext.Set().Where(o => o.Id != "1").ShardingFirstOrDefaultAsync(); + Assert.NotNull(sysUserMod); + Assert.True(sysUserMod.Id != "1"); + } + + [Fact] + public async Task FirstOrDefault5() + { + var sysUserMod = await _virtualDbContext.Set().Where(o => o.Name == "name_1001").ShardingFirstOrDefaultAsync(); + Assert.Null(sysUserMod); + } + + [Fact] + public async Task Count_Test() + { + var a = await _virtualDbContext.Set().Where(o => o.Name == "name_1000").ShardingCountAsync(); + Assert.Equal(1, a); + var b = await _virtualDbContext.Set().Where(o => o.Name != "name_1000").ShardingCountAsync(); + Assert.Equal(999, b); + } + + [Fact] + public async Task Sum_Test() + { + var a = await _virtualDbContext.Set().ShardingSumAsync(o => o.Age); + var expected = 0; + for (int i = 1; i <= 1000; i++) + { + expected += i; + } + + Assert.Equal(expected, a); + var b = await _virtualDbContext.Set().Where(o => o.Name != "name_1000").ShardingSumAsync(o => o.Age); + Assert.Equal(expected - 1000, b); + } + + [Fact] + public async Task Max_Test() + { + var a = await _virtualDbContext.Set().ShardingMaxAsync(o => o.Age); + Assert.Equal(1000, a); + var b = await _virtualDbContext.Set().Where(o => o.Name != "name_1000").ShardingMaxAsync(o => o.Age); + Assert.Equal(999, b); + var c = await _virtualDbContext.Set().Where(o => o.Age < 500).ShardingMaxAsync(o => o.Age); + Assert.Equal(499, c); + var e = await _virtualDbContext.Set().Where(o => o.Age <= 500).ShardingMaxAsync(o => o.Age); + Assert.Equal(500, e); + } + + [Fact] + public async Task Max_Join_Test() + { + var queryable = (from u in _virtualDbContext.Set().Where(o => o.Id == "300") + join salary in _virtualDbContext.Set() + on u.Id equals salary.UserId + select new + { + Salary = salary.Salary, + DateOfMonth = salary.DateOfMonth, + Name = u.Name + }); + var maxSalary = await queryable.ShardingMaxAsync(o => o.Salary); + Assert.Equal(1390000, maxSalary); + } + + [Fact] + public async Task Min_Test() + { + var a = await _virtualDbContext.Set().ShardingMinAsync(o => o.Age); + Assert.Equal(1, a); + var b = await _virtualDbContext.Set().Where(o => o.Name != "name_1").ShardingMinAsync(o => o.Age); + Assert.Equal(2, b); + var c = await _virtualDbContext.Set().Where(o => o.Age > 500).ShardingMinAsync(o => o.Age); + Assert.Equal(501, c); + var e = await _virtualDbContext.Set().Where(o => o.Age >= 500).ShardingMinAsync(o => o.Age); + Assert.Equal(500, e); + } + + [Fact] + public async Task Any_Test() + { + var a = await _virtualDbContext.Set().ShardingAnyAsync(o => o.Age == 100); + Assert.True(a); + var b = await _virtualDbContext.Set().Where(o => o.Name != "name_1").ShardingAnyAsync(o => o.Age == 1); + Assert.False(b); + var c = await _virtualDbContext.Set().Where(o => o.Age > 500).ShardingAnyAsync(o => o.Age <= 500); + Assert.False(c); + var e = await _virtualDbContext.Set().Where(o => o.Age >= 500).ShardingAnyAsync(o => o.Age <= 500); + Assert.True(e); + } + + [Fact] + public async Task Group_Test() + { + var ids = new[] {"200", "300"}; + var dateOfMonths = new[] {202111, 202110}; + var group = await (from u in _virtualDbContext.Set() + .Where(o => ids.Contains(o.UserId) && dateOfMonths.Contains(o.DateOfMonth)) + group u by new + { + UId = u.UserId + } + into g + select new + { + GroupUserId = g.Key.UId, + Count = g.Count(), + TotalSalary = g.Sum(o => o.Salary), + AvgSalary = g.Average(o => o.Salary), + AvgSalaryDecimal = g.Average(o => o.SalaryDecimal), + MinSalary = g.Min(o => o.Salary), + MaxSalary = g.Max(o => o.Salary) + }).ToShardingListAsync(); + Assert.Equal(2, group.Count); + Assert.Equal(2, group[0].Count); + Assert.Equal(2260000, group[0].TotalSalary); + Assert.Equal(1130000, group[0].AvgSalary); + Assert.Equal(11300, group[0].AvgSalaryDecimal); + Assert.Equal(1120000, group[0].MinSalary); + Assert.Equal(1140000, group[0].MaxSalary); + } + [Fact] + public async Task Group_API_Test() + { + var ids = new[] {"200", "300"}; + var dateOfMonths = new[] {202111, 202110}; + var group = await _virtualDbContext.Set() + .Where(o => ids.Contains(o.UserId) && dateOfMonths.Contains(o.DateOfMonth)) + .ShardingGroupByAsync(g => new {UId = g.UserId}, g => new + { + + GroupUserId = g.Key.UId, + Count = g.Count(), + TotalSalary = g.Sum(o => o.Salary), + AvgSalary = g.Average(o => o.Salary), + AvgSalaryDecimal = g.Average(o => o.SalaryDecimal), + MinSalary = g.Min(o => o.Salary), + MaxSalary = g.Max(o => o.Salary) + }); + Assert.Equal(2, group.Count); + Assert.Equal(2, group[0].Count); + Assert.Equal(2260000, group[0].TotalSalary); + Assert.Equal(1130000, group[0].AvgSalary); + Assert.Equal(11300, group[0].AvgSalaryDecimal); + Assert.Equal(1120000, group[0].MinSalary); + Assert.Equal(1140000, group[0].MaxSalary); + } + } +} \ No newline at end of file diff --git a/test/ShardingCoreTestSqlServer3x/Shardings/SysUserModVirtualTableRoute.cs b/test/ShardingCoreTestSqlServer3x/Shardings/SysUserModVirtualTableRoute.cs new file mode 100644 index 00000000..3f065ae3 --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/Shardings/SysUserModVirtualTableRoute.cs @@ -0,0 +1,19 @@ +using ShardingCore.VirtualRoutes.Mods; +using ShardingCoreTestSqlServer3x.Domain.Entities; + +namespace ShardingCoreTestSqlServer3x.Shardings +{ +/* +* @Author: xjm +* @Description: +* @Date: Thursday, 14 January 2021 15:39:27 +* @Email: 326308290@qq.com +*/ + public class SysUserModVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualTableRoute + { + public SysUserModVirtualTableRoute() : base(2,3) + { + } + + } +} \ No newline at end of file diff --git a/test/ShardingCoreTestSqlServer3x/Shardings/SysUserSalaryVirtualTableRoute.cs b/test/ShardingCoreTestSqlServer3x/Shardings/SysUserSalaryVirtualTableRoute.cs new file mode 100644 index 00000000..d9451686 --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/Shardings/SysUserSalaryVirtualTableRoute.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Core.VirtualRoutes.TableRoutes; +using ShardingCoreTestSqlServer3x.Domain.Entities; + +namespace ShardingCoreTestSqlServer3x.Shardings +{ +/* +* @Author: xjm +* @Description: +* @Date: Monday, 01 February 2021 15:54:55 +* @Email: 326308290@qq.com +*/ + public class SysUserSalaryVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute + { + protected override int ConvertToShardingKey(object shardingKey) + { + return Convert.ToInt32(shardingKey); + } + + public override string ShardingKeyToTail(object shardingKey) + { + var time = ConvertToShardingKey(shardingKey); + return TimeFormatToTail(time); + } + + + public override List GetAllTails() + { + var beginTime = new DateTime(2020, 1, 1); + var endTime = new DateTime(2021, 12, 1); + var list = new List(24); + var tempTime = beginTime; + while (tempTime <= endTime) + { + list.Add($"{tempTime:yyyyMM}"); + tempTime = tempTime.AddMonths(1); + } + + return list; + } + + protected string TimeFormatToTail(int time) + { + var dateOfMonth=DateTime.ParseExact($"{time}","yyyyMM",System.Globalization.CultureInfo.InvariantCulture,System.Globalization.DateTimeStyles.AdjustToUniversal); + return $"{dateOfMonth:yyyyMM}"; + } + + protected override Expression> GetRouteToFilter(int shardingKey, ShardingOperatorEnum shardingOperator) + { + var t = TimeFormatToTail(shardingKey); + switch (shardingOperator) + { + case ShardingOperatorEnum.GreaterThan: + case ShardingOperatorEnum.GreaterThanOrEqual: + return tail => String.Compare(tail, t, StringComparison.Ordinal) >= 0; + case ShardingOperatorEnum.LessThan: + return tail => String.Compare(tail, t, StringComparison.Ordinal) < 0; + case ShardingOperatorEnum.LessThanOrEqual: + return tail => String.Compare(tail, t, StringComparison.Ordinal) <= 0; + case ShardingOperatorEnum.Equal: return tail => tail == t; + default: + { +#if DEBUG + Console.WriteLine($"shardingOperator is not equal scan all table tail"); +#endif + return tail => true; + } + } + } + } +} \ No newline at end of file diff --git a/test/ShardingCoreTestSqlServer3x/Startup.cs b/test/ShardingCoreTestSqlServer3x/Startup.cs new file mode 100644 index 00000000..86795507 --- /dev/null +++ b/test/ShardingCoreTestSqlServer3x/Startup.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using ShardingCore; +using ShardingCore.DbContexts.VirtualDbContexts; +using ShardingCore.Extensions; +using ShardingCore.SqlServer; +using ShardingCoreTestSqlServer3x.Domain.Entities; +using ShardingCoreTestSqlServer3x.Shardings; + +namespace ShardingCoreTestSqlServer3x +{ +/* +* @Author: xjm +* @Description: +* @Date: Friday, 15 January 2021 15:37:46 +* @Email: 326308290@qq.com +*/ + public class Startup + { + // // 自定义 host 构建 + public void ConfigureHost(IHostBuilder hostBuilder) + { + hostBuilder + .ConfigureAppConfiguration(builder => + { + builder.AddJsonFile("Configs/DbConfig.json"); + }); + } + + // 支持的形式: + // ConfigureServices(IServiceCollection services) + // ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext) + // ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services) + public void ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext) + { + services.AddShardingSqlServer(o => + { + o.EnsureCreatedWithOutShardingTable = true; + o.CreateShardingTableOnStart = true; + o.AddShardingDbContextWithShardingTable("conn1", hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"], dbConfig => + { + dbConfig.AddShardingTableRoute(); + dbConfig.AddShardingTableRoute(); + }); + //o.AddDataSourceVirtualRoute<>(); + + }); + } + + // 可以添加要用到的方法参数,会自动从注册的服务中获取服务实例,类似于 asp.net core 里 Configure 方法 + public void Configure(IServiceProvider serviceProvider) + { + var shardingBootstrapper = serviceProvider.GetService(); + shardingBootstrapper.Start(); + // 有一些测试数据要初始化可以放在这里 + InitData(serviceProvider).GetAwaiter().GetResult(); + } + + /// + /// 添加种子数据 + /// + /// + /// + private async Task InitData(IServiceProvider serviceProvider) + { + using (var scope = serviceProvider.CreateScope()) + { + var virtualDbContext = scope.ServiceProvider.GetService(); + if (!await virtualDbContext.Set().ShardingAnyAsync(o => true)) + { + var ids = Enumerable.Range(1, 1000); + var userMods = new List(); + var userSalaries = new List(); + var beginTime = new DateTime(2020, 1, 1); + var endTime = new DateTime(2021, 12, 1); + foreach (var id in ids) + { + userMods.Add(new SysUserMod() + { + Id = id.ToString(), + Age = id, + Name = $"name_{id}", + AgeGroup=Math.Abs(id%10) + }); + var tempTime = beginTime; + var i = 0; + while (tempTime<=endTime) + { + var dateOfMonth = $@"{tempTime:yyyyMM}"; + userSalaries.Add(new SysUserSalary() + { + Id = $@"{id}{dateOfMonth}", + UserId = id.ToString(), + DateOfMonth = int.Parse(dateOfMonth), + Salary = 700000+id*100*i, + SalaryLong = 700000+id*100*i, + SalaryDecimal = (700000+id*100*i)/100m, + SalaryDouble = (700000+id*100*i)/100d, + SalaryFloat = (700000+id*100*i)/100f + }); + tempTime=tempTime.AddMonths(1); + i++; + } + } + + await virtualDbContext.InsertRangeAsync(userMods); + await virtualDbContext.InsertRangeAsync(userSalaries); + + await virtualDbContext.SaveChangesAsync(); + } + } + } + } +} \ No newline at end of file