From c0d9c1f99e04da98ccc04fcb67e0ea9644e32e82 Mon Sep 17 00:00:00 2001 From: Coldairarrow <862520575@qq.com> Date: Sun, 27 Sep 2020 22:19:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=86=E8=A1=A8=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/Demo.DbMigrator/CustomContext.cs | 2 +- .../DesignTimeDbContextFactory.cs | 12 +- examples/Demo.DbMigrator/Entities/Order.cs | 2 +- .../20200926034223_InitialCreate.cs | 50 ------ ... 20200927141554_InitialCreate.Designer.cs} | 6 +- .../20200927141554_InitialCreate.cs | 155 ++++++++++++++++++ .../Migrations/CustomContextModelSnapshot.cs | 4 +- examples/Demo.DbMigrator/a.json | 1 - .../EFCoreShardingExtensions.cs | 2 +- .../DependencyInjection/ShardingContainer.cs | 13 +- .../Migrations/ShardingMigration.cs | 103 +++++++++++- src/EFCore.Sharding/Util/Extention.Object.cs | 10 +- 12 files changed, 292 insertions(+), 68 deletions(-) delete mode 100644 examples/Demo.DbMigrator/Migrations/20200926034223_InitialCreate.cs rename examples/Demo.DbMigrator/Migrations/{20200926034223_InitialCreate.Designer.cs => 20200927141554_InitialCreate.Designer.cs} (94%) create mode 100644 examples/Demo.DbMigrator/Migrations/20200927141554_InitialCreate.cs delete mode 100644 examples/Demo.DbMigrator/a.json diff --git a/examples/Demo.DbMigrator/CustomContext.cs b/examples/Demo.DbMigrator/CustomContext.cs index 4654df3..f93cc36 100644 --- a/examples/Demo.DbMigrator/CustomContext.cs +++ b/examples/Demo.DbMigrator/CustomContext.cs @@ -1,6 +1,6 @@ using EFCore.Sharding; -namespace DbMigrator +namespace Demo.DbMigrator { public class CustomContext : GenericDbContext { diff --git a/examples/Demo.DbMigrator/DesignTimeDbContextFactory.cs b/examples/Demo.DbMigrator/DesignTimeDbContextFactory.cs index 4754e33..00199ff 100644 --- a/examples/Demo.DbMigrator/DesignTimeDbContextFactory.cs +++ b/examples/Demo.DbMigrator/DesignTimeDbContextFactory.cs @@ -3,16 +3,24 @@ using Microsoft.Extensions.DependencyInjection; using System; -namespace DbMigrator +namespace Demo.DbMigrator { public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory { + private static readonly string _connectionString = "Data Source=localhost;Initial Catalog=DbMigrator;Integrated Security=True"; + static DesignTimeDbContextFactory() { + DateTime startTime = DateTime.Now.AddMinutes(-5); ServiceCollection services = new ServiceCollection(); services.AddEFCoreSharding(x => { x.MigrationsWithoutForeignKey(); + //添加数据源 + x.AddDataSource(_connectionString, ReadWriteType.Read | ReadWriteType.Write, DatabaseType.SqlServer); + + //按分钟分表 + x.SetDateSharding(nameof(Order.CreateTime), ExpandByDateMode.PerMinute, startTime); }); ServiceProvider = services.BuildServiceProvider(); new EFCoreShardingBootstrapper(ServiceProvider).StartAsync(default).Wait(); @@ -31,7 +39,7 @@ public CustomContext CreateDbContext(string[] args) .GetService() .GetDbContext(new DbContextParamters { - ConnectionString = "Data Source=localhost;Initial Catalog=DbMigrator;Integrated Security=True", + ConnectionString = _connectionString, DbType = DatabaseType.SqlServer, }); diff --git a/examples/Demo.DbMigrator/Entities/Order.cs b/examples/Demo.DbMigrator/Entities/Order.cs index 62a7186..8187de6 100644 --- a/examples/Demo.DbMigrator/Entities/Order.cs +++ b/examples/Demo.DbMigrator/Entities/Order.cs @@ -10,7 +10,7 @@ public class Order { [Key] public Guid Id { get; set; } - public DateTime DateTime { get; set; } + public DateTime CreateTime { get; set; } public List OrderItems { get; set; } = new List(); } } diff --git a/examples/Demo.DbMigrator/Migrations/20200926034223_InitialCreate.cs b/examples/Demo.DbMigrator/Migrations/20200926034223_InitialCreate.cs deleted file mode 100644 index 62480eb..0000000 --- a/examples/Demo.DbMigrator/Migrations/20200926034223_InitialCreate.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Demo.DbMigrator.Migrations -{ - public partial class InitialCreate : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Order", - columns: table => new - { - Id = table.Column(nullable: false), - DateTime = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Order", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "OrderItem", - columns: table => new - { - Id = table.Column(nullable: false), - OrderId = table.Column(nullable: false), - Name = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OrderItem", x => x.Id); - }); - - migrationBuilder.CreateIndex( - name: "IX_OrderItem_OrderId", - table: "OrderItem", - column: "OrderId"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "OrderItem"); - - migrationBuilder.DropTable( - name: "Order"); - } - } -} diff --git a/examples/Demo.DbMigrator/Migrations/20200926034223_InitialCreate.Designer.cs b/examples/Demo.DbMigrator/Migrations/20200927141554_InitialCreate.Designer.cs similarity index 94% rename from examples/Demo.DbMigrator/Migrations/20200926034223_InitialCreate.Designer.cs rename to examples/Demo.DbMigrator/Migrations/20200927141554_InitialCreate.Designer.cs index af9adfc..7aee51a 100644 --- a/examples/Demo.DbMigrator/Migrations/20200926034223_InitialCreate.Designer.cs +++ b/examples/Demo.DbMigrator/Migrations/20200927141554_InitialCreate.Designer.cs @@ -1,6 +1,6 @@ // using System; -using DbMigrator; +using Demo.DbMigrator; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; @@ -10,7 +10,7 @@ namespace Demo.DbMigrator.Migrations { [DbContext(typeof(CustomContext))] - [Migration("20200926034223_InitialCreate")] + [Migration("20200927141554_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -27,7 +27,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("DateTime") + b.Property("CreateTime") .HasColumnType("datetime2"); b.HasKey("Id"); diff --git a/examples/Demo.DbMigrator/Migrations/20200927141554_InitialCreate.cs b/examples/Demo.DbMigrator/Migrations/20200927141554_InitialCreate.cs new file mode 100644 index 0000000..6cbcfa6 --- /dev/null +++ b/examples/Demo.DbMigrator/Migrations/20200927141554_InitialCreate.cs @@ -0,0 +1,155 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Demo.DbMigrator.Migrations +{ + public partial class InitialCreate : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Order_202009272210", + columns: table => new + { + Id = table.Column(nullable: false), + CreateTime = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Order_202009272210", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Order_202009272211", + columns: table => new + { + Id = table.Column(nullable: false), + CreateTime = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Order_202009272211", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Order_202009272212", + columns: table => new + { + Id = table.Column(nullable: false), + CreateTime = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Order_202009272212", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Order_202009272213", + columns: table => new + { + Id = table.Column(nullable: false), + CreateTime = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Order_202009272213", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Order_202009272214", + columns: table => new + { + Id = table.Column(nullable: false), + CreateTime = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Order_202009272214", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Order_202009272215", + columns: table => new + { + Id = table.Column(nullable: false), + CreateTime = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Order_202009272215", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Order_202009272216", + columns: table => new + { + Id = table.Column(nullable: false), + CreateTime = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Order_202009272216", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Order_202009272217", + columns: table => new + { + Id = table.Column(nullable: false), + CreateTime = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Order_202009272217", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OrderItem", + columns: table => new + { + Id = table.Column(nullable: false), + OrderId = table.Column(nullable: false), + Name = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderItem", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_OrderItem_OrderId", + table: "OrderItem", + column: "OrderId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OrderItem"); + + migrationBuilder.DropTable( + name: "Order_202009272210"); + + migrationBuilder.DropTable( + name: "Order_202009272211"); + + migrationBuilder.DropTable( + name: "Order_202009272212"); + + migrationBuilder.DropTable( + name: "Order_202009272213"); + + migrationBuilder.DropTable( + name: "Order_202009272214"); + + migrationBuilder.DropTable( + name: "Order_202009272215"); + + migrationBuilder.DropTable( + name: "Order_202009272216"); + + migrationBuilder.DropTable( + name: "Order_202009272217"); + } + } +} diff --git a/examples/Demo.DbMigrator/Migrations/CustomContextModelSnapshot.cs b/examples/Demo.DbMigrator/Migrations/CustomContextModelSnapshot.cs index 96d35fc..75732c5 100644 --- a/examples/Demo.DbMigrator/Migrations/CustomContextModelSnapshot.cs +++ b/examples/Demo.DbMigrator/Migrations/CustomContextModelSnapshot.cs @@ -1,6 +1,6 @@ // using System; -using DbMigrator; +using Demo.DbMigrator; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; @@ -25,7 +25,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("DateTime") + b.Property("CreateTime") .HasColumnType("datetime2"); b.HasKey("Id"); diff --git a/examples/Demo.DbMigrator/a.json b/examples/Demo.DbMigrator/a.json deleted file mode 100644 index 901c814..0000000 --- a/examples/Demo.DbMigrator/a.json +++ /dev/null @@ -1 +0,0 @@ -{"Name":"Order","Schema":null,"PrimaryKey":{"Schema":null,"Table":"Order","Name":"PK_Order","Columns":["Id"],"IsDestructiveChange":false},"Columns":[{"Name":"Id","Schema":null,"Table":"Order","ClrType":"System.Guid, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","ColumnType":null,"IsUnicode":null,"IsFixedLength":false,"MaxLength":null,"IsRowVersion":false,"IsNullable":false,"DefaultValue":null,"DefaultValueSql":null,"ComputedColumnSql":null,"Comment":null,"IsDestructiveChange":false},{"Name":"DateTime","Schema":null,"Table":"Order","ClrType":"System.DateTime, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","ColumnType":null,"IsUnicode":null,"IsFixedLength":false,"MaxLength":null,"IsRowVersion":false,"IsNullable":false,"DefaultValue":null,"DefaultValueSql":null,"ComputedColumnSql":null,"Comment":null,"IsDestructiveChange":false}],"ForeignKeys":[],"UniqueConstraints":[],"CheckConstraints":[],"Comment":null,"IsDestructiveChange":false}{"Name":"OrderItem","Schema":null,"PrimaryKey":{"Schema":null,"Table":"OrderItem","Name":"PK_OrderItem","Columns":["Id"],"IsDestructiveChange":false},"Columns":[{"Name":"Id","Schema":null,"Table":"OrderItem","ClrType":"System.Guid, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","ColumnType":null,"IsUnicode":null,"IsFixedLength":false,"MaxLength":null,"IsRowVersion":false,"IsNullable":false,"DefaultValue":null,"DefaultValueSql":null,"ComputedColumnSql":null,"Comment":null,"IsDestructiveChange":false},{"Name":"OrderId","Schema":null,"Table":"OrderItem","ClrType":"System.Guid, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","ColumnType":null,"IsUnicode":null,"IsFixedLength":false,"MaxLength":null,"IsRowVersion":false,"IsNullable":false,"DefaultValue":null,"DefaultValueSql":null,"ComputedColumnSql":null,"Comment":null,"IsDestructiveChange":false},{"Name":"Name","Schema":null,"Table":"OrderItem","ClrType":"System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","ColumnType":null,"IsUnicode":null,"IsFixedLength":false,"MaxLength":null,"IsRowVersion":false,"IsNullable":true,"DefaultValue":null,"DefaultValueSql":null,"ComputedColumnSql":null,"Comment":null,"IsDestructiveChange":false}],"ForeignKeys":[],"UniqueConstraints":[],"CheckConstraints":[],"Comment":null,"IsDestructiveChange":false} \ No newline at end of file diff --git a/src/EFCore.Sharding/DependencyInjection/EFCoreShardingExtensions.cs b/src/EFCore.Sharding/DependencyInjection/EFCoreShardingExtensions.cs index 25d7586..46640c1 100644 --- a/src/EFCore.Sharding/DependencyInjection/EFCoreShardingExtensions.cs +++ b/src/EFCore.Sharding/DependencyInjection/EFCoreShardingExtensions.cs @@ -21,7 +21,7 @@ public static IServiceCollection AddEFCoreSharding(this IServiceCollection servi ShardingContainer container = new ShardingContainer(services); shardingBuilder?.Invoke(container); - + services.AddSingleton(container); services.AddSingleton(container); services.AddSingleton(container); services.AddSingleton(); diff --git a/src/EFCore.Sharding/DependencyInjection/ShardingContainer.cs b/src/EFCore.Sharding/DependencyInjection/ShardingContainer.cs index 7052cbe..3a95acd 100644 --- a/src/EFCore.Sharding/DependencyInjection/ShardingContainer.cs +++ b/src/EFCore.Sharding/DependencyInjection/ShardingContainer.cs @@ -140,6 +140,8 @@ public DatabaseType FindADbType() { return _dataSources.FirstOrDefault().DbType; } + public readonly Dictionary> ExistsShardingTables + = new Dictionary>(); #endregion @@ -301,6 +303,15 @@ public IShardingBuilder SetDateSharding(string shardingField, ExpandByD { var theSourceName = GetSourceName(theTime); string suffix = shardingRule.GetTableSuffixByField(theTime); + + string absTableName = AnnotationHelper.GetDbTableName(typeof(TEntity)); + string fullTableName = $"{absTableName}_{suffix}"; + if (!ExistsShardingTables.ContainsKey(absTableName)) + { + ExistsShardingTables.Add(absTableName, new List()); + } + ExistsShardingTables[absTableName].Add(fullTableName); + CreateTable(serviceProvider, theSourceName, suffix); AddPhysicTable(suffix, theSourceName); @@ -308,7 +319,6 @@ public IShardingBuilder SetDateSharding(string shardingField, ExpandByD } //定时自动建表 - JobHelper.SetCronJob(() => { DateTime trueDate = DateTime.Now + paramter.leadTime; @@ -319,7 +329,6 @@ public IShardingBuilder SetDateSharding(string shardingField, ExpandByD AddPhysicTable(suffix, theSourceName); }, paramter.conExpression); - string GetSourceName(DateTime time) { return ranges.Where(x => time >= x.startTime && time < x.endTime).FirstOrDefault().sourceName; diff --git a/src/EFCore.Sharding/Migrations/ShardingMigration.cs b/src/EFCore.Sharding/Migrations/ShardingMigration.cs index 484d1b6..8f9ceb3 100644 --- a/src/EFCore.Sharding/Migrations/ShardingMigration.cs +++ b/src/EFCore.Sharding/Migrations/ShardingMigration.cs @@ -10,17 +10,21 @@ using Microsoft.EntityFrameworkCore.Update.Internal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using Newtonsoft.Json; +using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; namespace EFCore.Sharding { [SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "<挂起>")] internal class ShardingMigration : MigrationsModelDiffer { + private readonly Dictionary> _existsShardingTables + = new Dictionary>(); public ShardingMigration( IRelationalTypeMappingSource typeMappingSource, IMigrationsAnnotationProvider migrationsAnnotations, @@ -30,6 +34,7 @@ CommandBatchPreparerDependencies commandBatchPreparerDependencies ) : base(typeMappingSource, migrationsAnnotations, changeDetector, updateAdapterFactory, commandBatchPreparerDependencies) { + _existsShardingTables = Cache.ServiceProvider.GetService().ExistsShardingTables; } public override IReadOnlyList GetDifferences(IModel source, IModel target) @@ -48,13 +53,103 @@ public override IReadOnlyList GetDifferences(IModel source, operation.ForeignKeys?.Clear(); } } - resList.AddRange(sourceOperations); //分表 - + resList.AddRange(sourceOperations.SelectMany(x => BuildShardingOperation(x))); return resList; } + + private List BuildShardingOperation(MigrationOperation sourceOperation) + { + //所有MigrationOperation定义 + //https://github.com/dotnet/efcore/tree/b970bf29a46521f40862a01db9e276e6448d3cb0/src/EFCore.Relational/Migrations/Operations + //ColumnOperation仅替换Table + //其余其余都是将Name和Table使用分表名替换 + + List resList = new List(); + string absTableName = string.Empty; + + string name = sourceOperation.GetPropertyValue("Name") as string; + string tableName = sourceOperation.GetPropertyValue("Table") as string; + string pattern = string.Format("^({0})$|^({0}_.*?)$|^(.*?_{0}_.*?)$|^(.*?_{0})$", absTableName); + Func>, bool> where = x => + _existsShardingTables.Any(x => Regex.IsMatch(name, BuildPattern(x.Key))); + + if (!tableName.IsNullOrEmpty()) + { + absTableName = tableName; + } + else if (!name.IsNullOrEmpty() && _existsShardingTables.Any(x => where(x))) + { + absTableName = _existsShardingTables.Where(x => where(x)).FirstOrDefault().Key; + } + + //分表 + if (!absTableName.IsNullOrEmpty() && _existsShardingTables.ContainsKey(absTableName)) + { + var shardings = _existsShardingTables[absTableName]; + shardings.ForEach(aSharding => + { + var clone = sourceOperation.DeepClone(); + resList.Add(clone); + ReplaceName(clone, absTableName, aSharding); + }); + } + //不分表 + else + { + resList.Add(sourceOperation); + } + + return resList; + + string BuildPattern(string absTableName) + { + return string.Format("^({0})$|^({0}_.*?)$|^(.*?_{0}_.*?)$|^(.*?_{0})$", absTableName); + } + } + + private void ReplaceName(MigrationOperation theOperation, string sourceName, string targetName) + { + string name = theOperation.GetPropertyValue("Name") as string; + string tableName = theOperation.GetPropertyValue("TableName") as string; + if (!tableName.IsNullOrEmpty()) + { + theOperation.SetPropertyValue("Table", targetName); + } + if (!name.IsNullOrEmpty() && !(theOperation is ColumnOperation)) + { + theOperation.SetPropertyValue("Name", name.Replace(sourceName, targetName)); + } + Func propertyWhere = x => + x.PropertyType.IsGenericType + && x.PropertyType.GetGenericTypeDefinition() == typeof(List<>) + && typeof(MigrationOperation).IsAssignableFrom(x.PropertyType.GetGenericArguments()[0]); + //其它 + theOperation.GetType().GetProperties() + .Where(x => x.Name != "Name" + && x.Name != "Table" + && x.PropertyType != typeof(object) + && (typeof(MigrationOperation).IsAssignableFrom(x.PropertyType) || propertyWhere(x)) + ) + .ToList() + .ForEach(aProperty => + { + var propertyValue = aProperty.GetValue(theOperation); + if (propertyValue is MigrationOperation propertyOperation) + { + ReplaceName(propertyOperation, sourceName, targetName); + } + else if (propertyWhere(aProperty)) + { + foreach (var aValue in (IEnumerable)propertyValue) + { + ReplaceName((MigrationOperation)aValue, sourceName, targetName); + } + } + }); + } } } diff --git a/src/EFCore.Sharding/Util/Extention.Object.cs b/src/EFCore.Sharding/Util/Extention.Object.cs index 8e5acf9..a6dc84f 100644 --- a/src/EFCore.Sharding/Util/Extention.Object.cs +++ b/src/EFCore.Sharding/Util/Extention.Object.cs @@ -59,7 +59,15 @@ public static string ToJson(this object obj) /// public static object GetPropertyValue(this object obj, string propertyName) { - return obj.GetType().GetProperty(propertyName, _bindingFlags).GetValue(obj); + var property = obj.GetType().GetProperty(propertyName, _bindingFlags); + if (property != null) + { + return obj.GetType().GetProperty(propertyName, _bindingFlags).GetValue(obj); + } + else + { + return null; + } } ///