diff --git a/ShardingCore.sln b/ShardingCore.sln index ca7bc3e8..97005a1f 100644 --- a/ShardingCore.sln +++ b/ShardingCore.sln @@ -57,6 +57,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.ShardingConsole", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.OracleIssue", "samples\Sample.OracleIssue\Sample.OracleIssue.csproj", "{BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src2_6", "src2_6", "{5C015EB7-678C-421E-BB84-30EDCCCE0AB6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.2_6", "src2_6\ShardingCore.2_6\ShardingCore.2_6.csproj", "{B8B27249-E775-4A8F-BF64-B64C6B34DAA3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.Test6x", "test\ShardingCore.Test6x\ShardingCore.Test6x.csproj", "{8ED3A191-5A29-4599-B383-9FD225CC0BA2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -155,6 +161,14 @@ Global {BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D}.Debug|Any CPU.Build.0 = Debug|Any CPU {BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D}.Release|Any CPU.ActiveCfg = Release|Any CPU {BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D}.Release|Any CPU.Build.0 = Release|Any CPU + {B8B27249-E775-4A8F-BF64-B64C6B34DAA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8B27249-E775-4A8F-BF64-B64C6B34DAA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8B27249-E775-4A8F-BF64-B64C6B34DAA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8B27249-E775-4A8F-BF64-B64C6B34DAA3}.Release|Any CPU.Build.0 = Release|Any CPU + {8ED3A191-5A29-4599-B383-9FD225CC0BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8ED3A191-5A29-4599-B383-9FD225CC0BA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8ED3A191-5A29-4599-B383-9FD225CC0BA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8ED3A191-5A29-4599-B383-9FD225CC0BA2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -183,6 +197,8 @@ Global {3E895438-E609-4860-8391-6897ED55DA06} = {CC2C88C0-65F2-445D-BE78-973B840FE281} {BAB101D4-2BFF-44CE-90E3-8B72DDB266B8} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73} {BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73} + {B8B27249-E775-4A8F-BF64-B64C6B34DAA3} = {5C015EB7-678C-421E-BB84-30EDCCCE0AB6} + {8ED3A191-5A29-4599-B383-9FD225CC0BA2} = {CC2C88C0-65F2-445D-BE78-973B840FE281} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8C07A667-E8B4-43C7-8053-721584BAD291} diff --git a/benchmarks/ShardingCoreBenchmark/ShardingCoreBenchmark.csproj b/benchmarks/ShardingCoreBenchmark/ShardingCoreBenchmark.csproj index c74474c3..5e8de405 100644 --- a/benchmarks/ShardingCoreBenchmark/ShardingCoreBenchmark.csproj +++ b/benchmarks/ShardingCoreBenchmark/ShardingCoreBenchmark.csproj @@ -17,7 +17,7 @@ - + diff --git a/benchmarks/ShardingCoreBenchmark5x/ShardingCoreBenchmark5x.csproj b/benchmarks/ShardingCoreBenchmark5x/ShardingCoreBenchmark5x.csproj index 197d1354..3cdc6e75 100644 --- a/benchmarks/ShardingCoreBenchmark5x/ShardingCoreBenchmark5x.csproj +++ b/benchmarks/ShardingCoreBenchmark5x/ShardingCoreBenchmark5x.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/nuget-publish.bat b/nuget-publish.bat index 152fef52..4c3631d0 100644 --- a/nuget-publish.bat +++ b/nuget-publish.bat @@ -1,6 +1,7 @@ :start ::定义版本 -set SHARDINGCORE=6.8.0.5 +set SHARDINGCORE7=7.0.0.1 +set SHARDINGCORE2_6=6.8.0.5 ::删除所有bin与obj下的文件 @echo off diff --git a/samples/Sample.AutoCreateIfPresent/Sample.AutoCreateIfPresent.csproj b/samples/Sample.AutoCreateIfPresent/Sample.AutoCreateIfPresent.csproj index f8cd0e56..128682ae 100644 --- a/samples/Sample.AutoCreateIfPresent/Sample.AutoCreateIfPresent.csproj +++ b/samples/Sample.AutoCreateIfPresent/Sample.AutoCreateIfPresent.csproj @@ -13,7 +13,7 @@ - + diff --git a/samples/Sample.BulkConsole/Sample.BulkConsole.csproj b/samples/Sample.BulkConsole/Sample.BulkConsole.csproj index e0e13412..c4f054d1 100644 --- a/samples/Sample.BulkConsole/Sample.BulkConsole.csproj +++ b/samples/Sample.BulkConsole/Sample.BulkConsole.csproj @@ -10,7 +10,7 @@ - + diff --git a/samples/Sample.Migrations/RemoveForeignKeyMigrationsModelDiffer.cs b/samples/Sample.Migrations/RemoveForeignKeyMigrationsModelDiffer.cs index 4e8898d2..ef277dc4 100644 --- a/samples/Sample.Migrations/RemoveForeignKeyMigrationsModelDiffer.cs +++ b/samples/Sample.Migrations/RemoveForeignKeyMigrationsModelDiffer.cs @@ -22,9 +22,6 @@ namespace Sample.Migrations [SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "<挂起>")] public class RemoveForeignKeyMigrationsModelDiffer:MigrationsModelDiffer { - public RemoveForeignKeyMigrationsModelDiffer(IRelationalTypeMappingSource typeMappingSource, IMigrationsAnnotationProvider migrationsAnnotations, IChangeDetector changeDetector, IUpdateAdapterFactory updateAdapterFactory, CommandBatchPreparerDependencies commandBatchPreparerDependencies) : base(typeMappingSource, migrationsAnnotations, changeDetector, updateAdapterFactory, commandBatchPreparerDependencies) - { - } public override IReadOnlyList GetDifferences(IRelationalModel? source, IRelationalModel? target) { @@ -36,5 +33,9 @@ namespace Sample.Migrations } return sourceOperations; } + + public RemoveForeignKeyMigrationsModelDiffer(IRelationalTypeMappingSource typeMappingSource, IMigrationsAnnotationProvider migrationsAnnotations, IChangeDetector changeDetector, IUpdateAdapterFactory updateAdapterFactory, CommandBatchPreparerDependencies commandBatchPreparerDependencies) : base(typeMappingSource, migrationsAnnotations, changeDetector, updateAdapterFactory, commandBatchPreparerDependencies) + { + } } } \ No newline at end of file diff --git a/samples/Sample.Migrations/Sample.Migrations.csproj b/samples/Sample.Migrations/Sample.Migrations.csproj index a5bba585..bcaeb1ae 100644 --- a/samples/Sample.Migrations/Sample.Migrations.csproj +++ b/samples/Sample.Migrations/Sample.Migrations.csproj @@ -13,7 +13,7 @@ - + diff --git a/samples/Sample.MultiConfig/Sample.MultiConfig.csproj b/samples/Sample.MultiConfig/Sample.MultiConfig.csproj index 949ce3e5..f384fd36 100644 --- a/samples/Sample.MultiConfig/Sample.MultiConfig.csproj +++ b/samples/Sample.MultiConfig/Sample.MultiConfig.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/Sample.MySql/Sample.MySql.csproj b/samples/Sample.MySql/Sample.MySql.csproj index 7d8e1180..8c24e2c5 100644 --- a/samples/Sample.MySql/Sample.MySql.csproj +++ b/samples/Sample.MySql/Sample.MySql.csproj @@ -16,7 +16,7 @@ - + diff --git a/samples/Sample.NoShardingMultiLevel/Sample.NoShardingMultiLevel.csproj b/samples/Sample.NoShardingMultiLevel/Sample.NoShardingMultiLevel.csproj index c95271a4..4fdafaa8 100644 --- a/samples/Sample.NoShardingMultiLevel/Sample.NoShardingMultiLevel.csproj +++ b/samples/Sample.NoShardingMultiLevel/Sample.NoShardingMultiLevel.csproj @@ -11,7 +11,7 @@ - + diff --git a/samples/Sample.OracleIssue/Sample.OracleIssue.csproj b/samples/Sample.OracleIssue/Sample.OracleIssue.csproj index d3b78241..52b19498 100644 --- a/samples/Sample.OracleIssue/Sample.OracleIssue.csproj +++ b/samples/Sample.OracleIssue/Sample.OracleIssue.csproj @@ -15,7 +15,7 @@ - + diff --git a/samples/Sample.ShardingConsole/Sample.ShardingConsole.csproj b/samples/Sample.ShardingConsole/Sample.ShardingConsole.csproj index 9c8c6ace..be9b0f53 100644 --- a/samples/Sample.ShardingConsole/Sample.ShardingConsole.csproj +++ b/samples/Sample.ShardingConsole/Sample.ShardingConsole.csproj @@ -8,7 +8,7 @@ - + diff --git a/samples/Sample.SqlServer/Sample.SqlServer.csproj b/samples/Sample.SqlServer/Sample.SqlServer.csproj index 6bd6e59f..47eec57f 100644 --- a/samples/Sample.SqlServer/Sample.SqlServer.csproj +++ b/samples/Sample.SqlServer/Sample.SqlServer.csproj @@ -16,7 +16,7 @@ - + diff --git a/samples/Sample.SqlServer/UnionAllMerge/ShardingCoreSqlServerExtension.cs b/samples/Sample.SqlServer/UnionAllMerge/ShardingCoreSqlServerExtension.cs index cd5c9da0..9e28aa55 100644 --- a/samples/Sample.SqlServer/UnionAllMerge/ShardingCoreSqlServerExtension.cs +++ b/samples/Sample.SqlServer/UnionAllMerge/ShardingCoreSqlServerExtension.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.Internal; using ShardingCore.Core.ShardingConfigurations; -using ShardingCore.EFCores.OptionsExtensions; +using ShardingCore.EFCores; using ShardingCore.Sharding.Abstractions; namespace Sample.SqlServer.UnionAllMerge diff --git a/samples/Sample.SqlServer3x/Sample.SqlServer3x.csproj b/samples/Sample.SqlServer3x/Sample.SqlServer3x.csproj index afba6721..19dde831 100644 --- a/samples/Sample.SqlServer3x/Sample.SqlServer3x.csproj +++ b/samples/Sample.SqlServer3x/Sample.SqlServer3x.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/samples/Sample.SqlServerShardingAll/Sample.SqlServerShardingAll.csproj b/samples/Sample.SqlServerShardingAll/Sample.SqlServerShardingAll.csproj index f6c09ec1..9517e637 100644 --- a/samples/Sample.SqlServerShardingAll/Sample.SqlServerShardingAll.csproj +++ b/samples/Sample.SqlServerShardingAll/Sample.SqlServerShardingAll.csproj @@ -9,7 +9,7 @@ - + diff --git a/samples/Sample.SqlServerShardingDataSource/Sample.SqlServerShardingDataSource.csproj b/samples/Sample.SqlServerShardingDataSource/Sample.SqlServerShardingDataSource.csproj index 5aef5a2e..19fa28ac 100644 --- a/samples/Sample.SqlServerShardingDataSource/Sample.SqlServerShardingDataSource.csproj +++ b/samples/Sample.SqlServerShardingDataSource/Sample.SqlServerShardingDataSource.csproj @@ -11,7 +11,7 @@ - + diff --git a/samples/Sample.SqlServerShardingTable/Sample.SqlServerShardingTable.csproj b/samples/Sample.SqlServerShardingTable/Sample.SqlServerShardingTable.csproj index f6c09ec1..9517e637 100644 --- a/samples/Sample.SqlServerShardingTable/Sample.SqlServerShardingTable.csproj +++ b/samples/Sample.SqlServerShardingTable/Sample.SqlServerShardingTable.csproj @@ -9,7 +9,7 @@ - + diff --git a/samples/Samples.AbpSharding/AbstractShardingAbpDbContext.cs b/samples/Samples.AbpSharding/AbstractShardingAbpDbContext.cs index 2a86153e..27907862 100644 --- a/samples/Samples.AbpSharding/AbstractShardingAbpDbContext.cs +++ b/samples/Samples.AbpSharding/AbstractShardingAbpDbContext.cs @@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Threading; using System.Threading.Tasks; -using ShardingCore.EFCores.OptionsExtensions; +using ShardingCore.EFCores; using ShardingCore.Helpers; using ShardingCore.Utils; using Volo.Abp.Domain.Entities; diff --git a/samples/Samples.AbpSharding/AbstractShardingAbpZeroDbContext.cs b/samples/Samples.AbpSharding/AbstractShardingAbpZeroDbContext.cs index b2cb0748..ff81b29d 100644 --- a/samples/Samples.AbpSharding/AbstractShardingAbpZeroDbContext.cs +++ b/samples/Samples.AbpSharding/AbstractShardingAbpZeroDbContext.cs @@ -18,7 +18,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using ShardingCore.Core.VirtualDatabase.VirtualDataSources; using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; -using ShardingCore.EFCores.OptionsExtensions; +using ShardingCore.EFCores; using ShardingCore.Extensions; using ShardingCore.Sharding; using ShardingCore.Sharding.Abstractions; diff --git a/samples/Samples.AbpSharding/Samples.AbpSharding.csproj b/samples/Samples.AbpSharding/Samples.AbpSharding.csproj index b62ce798..87474750 100644 --- a/samples/Samples.AbpSharding/Samples.AbpSharding.csproj +++ b/samples/Samples.AbpSharding/Samples.AbpSharding.csproj @@ -11,7 +11,7 @@ - + diff --git a/samples/Samples.AutoByDate.SqlServer/RemoveForeignKeyMigrationsModelDiffer.cs b/samples/Samples.AutoByDate.SqlServer/RemoveForeignKeyMigrationsModelDiffer.cs index bdbac891..42a59e41 100644 --- a/samples/Samples.AutoByDate.SqlServer/RemoveForeignKeyMigrationsModelDiffer.cs +++ b/samples/Samples.AutoByDate.SqlServer/RemoveForeignKeyMigrationsModelDiffer.cs @@ -20,9 +20,6 @@ namespace Samples.AutoByDate.SqlServer { nameof(SysUserLog1ByDay) }; - public RemoveForeignKeyMigrationsModelDiffer(IRelationalTypeMappingSource typeMappingSource, IMigrationsAnnotationProvider migrationsAnnotations, IChangeDetector changeDetector, IUpdateAdapterFactory updateAdapterFactory, CommandBatchPreparerDependencies commandBatchPreparerDependencies) : base(typeMappingSource, migrationsAnnotations, changeDetector, updateAdapterFactory, commandBatchPreparerDependencies) - { - } public override IReadOnlyList GetDifferences(IRelationalModel? source, IRelationalModel? target) { @@ -34,5 +31,9 @@ namespace Samples.AutoByDate.SqlServer } return sourceOperations; } + + public RemoveForeignKeyMigrationsModelDiffer(IRelationalTypeMappingSource typeMappingSource, IMigrationsAnnotationProvider migrationsAnnotations, IChangeDetector changeDetector, IUpdateAdapterFactory updateAdapterFactory, CommandBatchPreparerDependencies commandBatchPreparerDependencies) : base(typeMappingSource, migrationsAnnotations, changeDetector, updateAdapterFactory, commandBatchPreparerDependencies) + { + } } } diff --git a/samples/Samples.AutoByDate.SqlServer/Samples.AutoByDate.SqlServer.csproj b/samples/Samples.AutoByDate.SqlServer/Samples.AutoByDate.SqlServer.csproj index 61fa7c10..37b0ff4c 100644 --- a/samples/Samples.AutoByDate.SqlServer/Samples.AutoByDate.SqlServer.csproj +++ b/samples/Samples.AutoByDate.SqlServer/Samples.AutoByDate.SqlServer.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/ShardingCore/EFCores/EFCore2x/ScriptMigrationGenerator.cs b/src/ShardingCore/EFCores/EFCore2x/ScriptMigrationGenerator.cs index e6c836c5..1f4e6a2f 100644 --- a/src/ShardingCore/EFCores/EFCore2x/ScriptMigrationGenerator.cs +++ b/src/ShardingCore/EFCores/EFCore2x/ScriptMigrationGenerator.cs @@ -1,4 +1,4 @@ -#if NETCOREAPP2_0 +#if NETCOREAPP2_0&&SHARDINGCORE2_6 using Microsoft.EntityFrameworkCore.Migrations; using ShardingCore.Core.RuntimeContexts; diff --git a/src/ShardingCore/EFCores/EFCore2x/ShardingChangeTracker.cs b/src/ShardingCore/EFCores/EFCore2x/ShardingChangeTracker.cs new file mode 100644 index 00000000..abf643d6 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore2x/ShardingChangeTracker.cs @@ -0,0 +1,111 @@ +#if NETCOREAPP2_0&&SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +using Microsoft.EntityFrameworkCore.Metadata; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + public class ShardingChangeTracker : ChangeTracker + { + private readonly DbContext _dbContext; + + public ShardingChangeTracker(DbContext context, IStateManager stateManager, IChangeDetector changeDetector, + IModel model, IEntityEntryGraphIterator graphIterator) : base(context, stateManager, changeDetector, model, + graphIterator) + { + _dbContext = context; + } + + public override bool HasChanges() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().Any(o => + o.Value.GetCurrentContexts().Any(r => r.Value.ChangeTracker.HasChanges())); + } + + return base.HasChanges(); + } + + public override IEnumerable Entries() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().SelectMany(o => + o.Value.GetCurrentContexts().SelectMany(cd => cd.Value.ChangeTracker.Entries())); + } + + return base.Entries(); + } + + public override IEnumerable> Entries() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().SelectMany(o => + o.Value.GetCurrentContexts().SelectMany(cd => cd.Value.ChangeTracker.Entries())); + } + + return base.Entries(); + } + + public override void DetectChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.DetectChanges()); + return; + } + base.DetectChanges(); + } + + public override void AcceptAllChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.AcceptAllChanges()); + return; + } + base.AcceptAllChanges(); + } + + private void Do(Action action) + { + var dataSourceDbContexts = ((IShardingDbContext)_dbContext).GetShardingExecutor().GetCurrentDbContexts(); + foreach (var dataSourceDbContext in dataSourceDbContexts) + { + var currentContexts = dataSourceDbContext.Value.GetCurrentContexts(); + foreach (var keyValuePair in currentContexts) + { + action(keyValuePair.Value.ChangeTracker); + } + } + } + + public override void TrackGraph(object rootEntity, Action callback) + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity); + genericDbContext.ChangeTracker.TrackGraph(rootEntity,callback); + // Do(c => c.TrackGraph(rootEntity,callback)); + } + } + + public override void TrackGraph(object rootEntity, TState state, Func callback) + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity); + genericDbContext.ChangeTracker.TrackGraph(rootEntity,state,callback); + // Do(c => c.TrackGraph(rootEntity,callback)); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore2x/ShardingDbSetSource.cs b/src/ShardingCore/EFCores/EFCore2x/ShardingDbSetSource.cs new file mode 100644 index 00000000..e4cf51b6 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore2x/ShardingDbSetSource.cs @@ -0,0 +1,62 @@ +#if NETCOREAPP2_0 && SHARDINGCORE2_6 +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; +using System; +using System.Collections.Concurrent; +using System.Reflection; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: Saturday, 14 August 2021 10:17:43 + * @Email: 326308290@qq.com + */ + + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public class ShardingDbSetSource : IDbSetSource, IDbQuerySource + { + private static readonly MethodInfo _genericCreateSet + = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); + + private static readonly MethodInfo _genericCreateQuery + = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateQueryFactory)); + + private readonly ConcurrentDictionary> _cache + = new ConcurrentDictionary>(); + + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public virtual object Create(DbContext context, Type type) + => CreateCore(context, type, _genericCreateSet); + + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public virtual object CreateQuery(DbContext context, Type type) + => CreateCore(context, type, _genericCreateQuery); + + private object CreateCore(DbContext context, Type type, MethodInfo createMethod) + => _cache.GetOrAdd( + type, + t => (Func)createMethod + .MakeGenericMethod(t) + .Invoke(null, null))(context); + + private static Func CreateSetFactory() + where TEntity : class + => c => new ShardingInternalDbSet(c); + + private static Func> CreateQueryFactory() + where TQuery : class + => c => new ShardingInternalDbQuery(c); + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/ShardingInternalDbQuery.cs b/src/ShardingCore/EFCores/EFCore2x/ShardingInternalDbQuery.cs similarity index 90% rename from src/ShardingCore/EFCores/ShardingInternalDbQuery.cs rename to src/ShardingCore/EFCores/EFCore2x/ShardingInternalDbQuery.cs index ec5cb4b7..38b171a2 100644 --- a/src/ShardingCore/EFCores/ShardingInternalDbQuery.cs +++ b/src/ShardingCore/EFCores/EFCore2x/ShardingInternalDbQuery.cs @@ -1,4 +1,5 @@ -using System; +#if NETCOREAPP2_0 && SHARDINGCORE2_6 +using System; using System.Collections.Generic; using System.Text; using Microsoft.EntityFrameworkCore; @@ -13,7 +14,7 @@ namespace ShardingCore.EFCores * @Ver: 1.0 * @Email: 326308290@qq.com */ -#if NETCOREAPP2_0 + public class ShardingInternalDbQuery : InternalDbQuery where TQuery : class { @@ -21,5 +22,6 @@ namespace ShardingCore.EFCores { } } -#endif + } +#endif diff --git a/src/ShardingCore/EFCores/EFCore2x/ShardingInternalDbSet.cs b/src/ShardingCore/EFCores/EFCore2x/ShardingInternalDbSet.cs new file mode 100644 index 00000000..43eba354 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore2x/ShardingInternalDbSet.cs @@ -0,0 +1,427 @@ +#if NETCOREAPP2_0 && SHARDINGCORE2_6 +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Internal; +using ShardingCore.Core; +using ShardingCore.Sharding.Abstractions; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Infrastructure; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Core.TrackerManagers; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualRoutes.Abstractions; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; +using ShardingCore.Utils; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/8/15 8:39:15 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + public class ShardingInternalDbSet : InternalDbSet + where TEntity : class + { + private readonly IShardingDbContext _context; + private readonly IShardingRuntimeContext _shardingRuntimeContext; + private LocalView? _localView; + + public ShardingInternalDbSet(DbContext context) : base(context) + { + _context = (IShardingDbContext)context; + _shardingRuntimeContext = context.GetShardingRuntimeContext(); + } + + private IDataSourceRouteManager _dataSourceRouteManager; + + protected IDataSourceRouteManager DataSourceRouteManager + { + get + { + if (null == _dataSourceRouteManager) + { + _dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager(); + } + + return _dataSourceRouteManager; + } + } + + public override LocalView Local + { + get + { + + if (((DbContext)_context).ChangeTracker.AutoDetectChangesEnabled) + { + ((DbContext)_context).ChangeTracker.DetectChanges(); + } + + return _localView ??= new ShardingLocalView(this); + } + } + private ITableRouteManager _tableRouteManager; + + protected ITableRouteManager TableRouteManager + { + get + { + if (null == _tableRouteManager) + { + _tableRouteManager = _shardingRuntimeContext.GetTableRouteManager(); + } + + return _tableRouteManager; + } + } + + private IEntityMetadataManager _entityMetadataManager; + + protected IEntityMetadataManager EntityMetadataManager + { + get + { + if (null == _entityMetadataManager) + { + _entityMetadataManager = _shardingRuntimeContext.GetEntityMetadataManager(); + } + + return _entityMetadataManager; + } + } + + private ITrackerManager _trackerManager; + + protected ITrackerManager TrackerManager + { + get + { + if (null == _trackerManager) + { + _trackerManager = _shardingRuntimeContext.GetTrackerManager(); + } + + return _trackerManager; + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Add(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Add(entity); + } + + public override async Task> AddAsync(TEntity entity, CancellationToken cancellationToken = + new CancellationToken()) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return await genericDbContext.Set().AddAsync(entity, cancellationToken); + } + + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Attach(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Attach(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Remove(TEntity entity) + { + Check.NotNull(entity, nameof(entity)); + + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Remove(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Update(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Update(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AddRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AddRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async Task AddRangeAsync(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + await aggregateKv.Key.Set().AddRangeAsync(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AttachRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AttachRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void RemoveRange(params TEntity[] entities) + { + Check.NotNull(entities, nameof(entities)); + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().RemoveRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void UpdateRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().UpdateRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AddRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AddRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async Task AddRangeAsync( + IEnumerable entities, + CancellationToken cancellationToken = default) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + await aggregateKv.Key.Set().AddRangeAsync(aggregateKv.Value, cancellationToken); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AttachRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AttachRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void RemoveRange(IEnumerable entities) + { + Check.NotNull(entities, nameof(entities)); + + + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().RemoveRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void UpdateRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().UpdateRange(aggregateKv.Value); + } + } + + private Dictionary> AggregateToDic(IEnumerable entities) + { + return entities.Select(o => + { + var dbContext = _context.CreateGenericDbContext(o); + return new + { + DbContext = dbContext, + Entity = o + }; + }).GroupBy(g => g.DbContext).ToDictionary(o => o.Key, o => o.Select(g => g.Entity)); + } + + public override TEntity Find(params object[] keyValues) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().Find(keyValues); + } + + return base.Find(keyValues); + } + + public override Task FindAsync(params object[] keyValues) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().FindAsync(keyValues); + } + return base.FindAsync(keyValues); + } + + public override Task FindAsync(object[] keyValues, CancellationToken cancellationToken) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().FindAsync(keyValues, cancellationToken); + } + return base.FindAsync(keyValues, cancellationToken); + } + + private DbContext GetDbContextByKeyValue(params object[] keyValues) + { + var entityMetadata = EntityMetadataManager.TryGet(typeof(TEntity)); + if (entityMetadata == null) + { + throw new ShardingCoreInvalidOperationException( + $"cant found type:[{typeof(TEntity)}] in {nameof(IEntityMetadataManager)}"); + } + + //既不是分表也不是分库的话就是默认对象 + if (!entityMetadata.IsShardingTable() && !entityMetadata.IsShardingDataSource()) + { + var defaultDataSourceName = _shardingRuntimeContext.GetVirtualDataSource().DefaultDataSourceName; + var routeTailFactory = _shardingRuntimeContext.GetRouteTailFactory(); + var routeTail = routeTailFactory.Create(string.Empty); + return _context.GetShareDbContext(defaultDataSourceName, routeTail); + } + + if (keyValues.Length == 1) + { + //单key字段 + if (entityMetadata.IsSingleKey) + { + var isShardingDataSource = entityMetadata.IsShardingDataSource(); + var shardingDataSourceFieldIsKey = entityMetadata.ShardingDataSourceFieldIsKey(); + if (isShardingDataSource && !shardingDataSourceFieldIsKey) + return null; + var isShardingTable = entityMetadata.IsShardingTable(); + var shardingTableFieldIsKey = entityMetadata.ShardingTableFieldIsKey(); + if (isShardingTable && !shardingTableFieldIsKey) + return null; + var primaryKeyValue = keyValues[0]; + if (primaryKeyValue != null) + { + var dataSourceName = GetDataSourceName(primaryKeyValue); + var realEntityType = TrackerManager.TranslateEntityType(typeof(TEntity)); + var tableTail = TableRouteManager.GetTableTail(dataSourceName, primaryKeyValue,realEntityType); + var routeTail = _shardingRuntimeContext.GetRouteTailFactory().Create(tableTail); + return _context.GetShareDbContext(dataSourceName, routeTail); + } + } + } + + return null; + } + + private string GetDataSourceName(object shardingKeyValue) + { + return DataSourceRouteManager.GetDataSourceName(shardingKeyValue); + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore2x/ShardingMigrator.cs b/src/ShardingCore/EFCores/EFCore2x/ShardingMigrator.cs new file mode 100644 index 00000000..e460538a --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore2x/ShardingMigrator.cs @@ -0,0 +1,48 @@ + +#if NETCOREAPP2_0&&SHARDINGCORE2_6 +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations.Internal; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Extensions; +using ShardingCore.Helpers; + +namespace ShardingCore.EFCores +{ + + public class ShardingMigrator:Migrator + { + private readonly IShardingRuntimeContext _shardingRuntimeContext; + + + public ShardingMigrator(IShardingRuntimeContext shardingRuntimeContext, IMigrationsAssembly migrationsAssembly, IHistoryRepository historyRepository, IDatabaseCreator databaseCreator, IMigrationsSqlGenerator migrationsSqlGenerator, IRawSqlCommandBuilder rawSqlCommandBuilder, IMigrationCommandExecutor migrationCommandExecutor, IRelationalConnection connection, ISqlGenerationHelper sqlGenerationHelper, IDiagnosticsLogger logger, IDatabaseProvider databaseProvider) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, logger, databaseProvider) + { + _shardingRuntimeContext = shardingRuntimeContext; + } + public override void Migrate(string targetMigration = null) + { + this.MigrateAsync(targetMigration).WaitAndUnwrapException(false); + // base.Migrate(targetMigration); + } + + public override async Task MigrateAsync(string targetMigration = null, CancellationToken cancellationToken = new CancellationToken()) + { + var virtualDataSource = _shardingRuntimeContext.GetVirtualDataSource(); + var allDataSourceNames = virtualDataSource.GetAllDataSourceNames(); + await DynamicShardingHelper.DynamicMigrateWithDataSourcesAsync(_shardingRuntimeContext, allDataSourceNames, null,targetMigration,cancellationToken).ConfigureAwait(false); + + } + public override string GenerateScript(string fromMigration = null, string toMigration = null, bool idempotent = false) + { + return new ScriptMigrationGenerator(_shardingRuntimeContext, fromMigration, toMigration, idempotent).GenerateScript(); + } + + } +} + +#endif diff --git a/src/ShardingCore/EFCores/EFCore2x/ShardingModelSource.cs b/src/ShardingCore/EFCores/EFCore2x/ShardingModelSource.cs index 582b6b06..d9fdecba 100644 --- a/src/ShardingCore/EFCores/EFCore2x/ShardingModelSource.cs +++ b/src/ShardingCore/EFCores/EFCore2x/ShardingModelSource.cs @@ -1,4 +1,4 @@ -#if NETCOREAPP2_0 +#if NETCOREAPP2_0&&SHARDINGCORE2_6 using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; diff --git a/src/ShardingCore/EFCores/EFCore2x/ShardingOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore2x/ShardingOptionsExtension.cs new file mode 100644 index 00000000..6aca963b --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore2x/ShardingOptionsExtension.cs @@ -0,0 +1,41 @@ + +#if NETCOREAPP2_0 && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Core; +using ShardingCore.Core.RuntimeContexts; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class ShardingOptionsExtension: IDbContextOptionsExtension + { + public IShardingRuntimeContext ShardingRuntimeContext { get; } + + public ShardingOptionsExtension(IShardingRuntimeContext shardingRuntimeContext) + { + ShardingRuntimeContext = shardingRuntimeContext; + } + public bool ApplyServices(IServiceCollection services) + { + services.AddSingleton(sp => ShardingRuntimeContext); + return true; + } + public long GetServiceProviderHashCode() => ShardingRuntimeContext.GetHashCode(); + + public void Validate(IDbContextOptions options) + { + } + + public string LogFragment => "ShardingOptionsExtension"; + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore2x/ShardingWrapOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore2x/ShardingWrapOptionsExtension.cs new file mode 100644 index 00000000..19e0d9a8 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore2x/ShardingWrapOptionsExtension.cs @@ -0,0 +1,37 @@ +#if NETCOREAPP2_0 && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Core; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + + + public class ShardingWrapOptionsExtension: IDbContextOptionsExtension + { + public bool ApplyServices(IServiceCollection services) + { + return false; + } + + public long GetServiceProviderHashCode() => 0; + + public void Validate(IDbContextOptions options) + { + } + + public string LogFragment => "ShardingWrapOptionsExtension"; + } +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore2x/Tx/ShardingRelationalTransaction.cs b/src/ShardingCore/EFCores/EFCore2x/Tx/ShardingRelationalTransaction.cs new file mode 100644 index 00000000..9ce5bbc6 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore2x/Tx/ShardingRelationalTransaction.cs @@ -0,0 +1,62 @@ +#if NETCOREAPP2_0&&SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Exceptions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/9/5 20:37:36 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class ShardingRelationalTransaction : RelationalTransaction + { + private readonly IShardingDbContext _shardingDbContext; + private readonly IShardingDbContextExecutor _shardingDbContextExecutor; + public ShardingRelationalTransaction(IShardingDbContext shardingDbContext, IRelationalConnection connection, DbTransaction transaction,IDiagnosticsLogger logger, bool transactionOwned) : base(connection, transaction, logger, transactionOwned) + { + _shardingDbContext = + shardingDbContext??throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); + _shardingDbContextExecutor = shardingDbContext.GetShardingExecutor() ?? + throw new ShardingCoreInvalidOperationException( + $"{shardingDbContext.GetType()} cant get {nameof(IShardingDbContextExecutor)} from {nameof(shardingDbContext.GetShardingExecutor)}"); + + } + + //protected override void ClearTransaction() + //{ + // if (_canClear) + // { + // base.ClearTransaction(); + // _supportShardingTransaction.NotifyShardingTransaction(null); + // } + //}f + public override void Commit() + { + base.Commit(); + _shardingDbContextExecutor.Commit(); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + + public override void Rollback() + { + base.Rollback(); + _shardingDbContextExecutor.Rollback(); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore2x/Tx/ShardingRelationalTransactionFactory.cs b/src/ShardingCore/EFCores/EFCore2x/Tx/ShardingRelationalTransactionFactory.cs new file mode 100644 index 00000000..fdadf322 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore2x/Tx/ShardingRelationalTransactionFactory.cs @@ -0,0 +1,49 @@ +#if NETCOREAPP2_0 && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Extensions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/9/5 16:03:04 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + + public class ShardingRelationalTransactionFactory : RelationalTransactionFactory where TShardingDbContext : DbContext, IShardingDbContext + { + private readonly RelationalTransactionFactoryDependencies _dependencies; + public ShardingRelationalTransactionFactory(RelationalTransactionFactoryDependencies dependencies) : base(dependencies) + { + _dependencies = dependencies; + } + public override RelationalTransaction Create(IRelationalConnection connection, DbTransaction transaction + , IDiagnosticsLogger logger, + bool transactionOwned) + { + var shardingDbContext = GetDbContext(connection) as IShardingDbContext; + return new ShardingRelationalTransaction(shardingDbContext, connection, transaction, logger, + transactionOwned); + } + + private DbContext GetDbContext(IRelationalConnection connection) + { + var namedConnectionStringResolver = ((RelationalConnectionDependencies)connection.GetPropertyValue("Dependencies")).ConnectionStringResolver; + var serviceProvider = (IServiceProvider)namedConnectionStringResolver.GetPropertyValue("ApplicationServiceProvider"); + var dbContext = (DbContext)serviceProvider.GetService(typeof(TShardingDbContext)); + return dbContext; + } + } +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore2x/Tx/ShardingRelationalTransactionManager.cs b/src/ShardingCore/EFCores/EFCore2x/Tx/ShardingRelationalTransactionManager.cs new file mode 100644 index 00000000..9031ef81 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore2x/Tx/ShardingRelationalTransactionManager.cs @@ -0,0 +1,101 @@ +#if NETCOREAPP2_0 && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/20 10:08:42 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + /// + /// manage transaction + /// + public class ShardingRelationalTransactionManager : IRelationalTransactionManager where TShardingDbContext : DbContext, IShardingDbContext + { + private readonly IRelationalConnection _relationalConnection; + private readonly IShardingDbContext _shardingDbContext; + private readonly IShardingDbContextExecutor _shardingDbContextExecutor; + + + + public ShardingRelationalTransactionManager(IRelationalConnection relationalConnection) + { + _relationalConnection = relationalConnection; + _shardingDbContext = GetDbContext(relationalConnection) as IShardingDbContext??throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); + _shardingDbContextExecutor = _shardingDbContext.GetShardingExecutor(); + } + private DbContext GetDbContext(IRelationalConnection connection) + { + var namedConnectionStringResolver = ((RelationalConnectionDependencies)connection.GetPropertyValue("Dependencies")).ConnectionStringResolver; + var serviceProvider = (IServiceProvider)namedConnectionStringResolver.GetPropertyValue("ApplicationServiceProvider"); + var dbContext = (DbContext)serviceProvider.GetService(typeof(TShardingDbContext)); + return dbContext; + } + + public void ResetState() + { + _relationalConnection.ResetState(); + } + + public IDbContextTransaction BeginTransaction() + { + return BeginTransaction(IsolationLevel.Unspecified); + } + + public Task BeginTransactionAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return BeginTransactionAsync(IsolationLevel.Unspecified, cancellationToken); + } + + public void CommitTransaction() + { + _relationalConnection.CommitTransaction(); + } + + + public void RollbackTransaction() + { + _relationalConnection.RollbackTransaction(); + } + + + public IDbContextTransaction CurrentTransaction => _relationalConnection.CurrentTransaction; + public IDbContextTransaction BeginTransaction(IsolationLevel isolationLevel) + { + var dbContextTransaction = _relationalConnection.BeginTransaction(isolationLevel); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public async Task BeginTransactionAsync(IsolationLevel isolationLevel, + CancellationToken cancellationToken = new CancellationToken()) + { + var dbContextTransaction = await _relationalConnection.BeginTransactionAsync(isolationLevel, cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public IDbContextTransaction UseTransaction(DbTransaction transaction) + { + var dbContextTransaction = _relationalConnection.UseTransaction(transaction); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + } +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore2x/UnionAllMergeOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore2x/UnionAllMergeOptionsExtension.cs new file mode 100644 index 00000000..ba870e3f --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore2x/UnionAllMergeOptionsExtension.cs @@ -0,0 +1,33 @@ +#if NETCOREAPP2_0 && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class UnionAllMergeOptionsExtension: IDbContextOptionsExtension + { + public bool ApplyServices(IServiceCollection services) + { + return false; + } + + public long GetServiceProviderHashCode() => 0; + + public void Validate(IDbContextOptions options) + { + } + + public string LogFragment => "UnionAllMergeOptionsExtension"; + } +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore3x/ScriptMigrationGenerator.cs b/src/ShardingCore/EFCores/EFCore3x/ScriptMigrationGenerator.cs index 72d85261..8704b5f7 100644 --- a/src/ShardingCore/EFCores/EFCore3x/ScriptMigrationGenerator.cs +++ b/src/ShardingCore/EFCores/EFCore3x/ScriptMigrationGenerator.cs @@ -1,4 +1,4 @@ -#if NETCOREAPP3_0 || NETSTANDARD2_0 || NETSTANDARD2_0 +#if (NETCOREAPP3_0 || NETSTANDARD2_0 )&&SHARDINGCORE2_6 using Microsoft.EntityFrameworkCore.Migrations; using ShardingCore.Core.RuntimeContexts; diff --git a/src/ShardingCore/EFCores/EFCore3x/ShardingChangeTracker.cs b/src/ShardingCore/EFCores/EFCore3x/ShardingChangeTracker.cs new file mode 100644 index 00000000..1d565221 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore3x/ShardingChangeTracker.cs @@ -0,0 +1,123 @@ +#if (NETCOREAPP3_0 || NETSTANDARD2_0 )&&SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +using Microsoft.EntityFrameworkCore.Metadata; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + public class ShardingChangeTracker : ChangeTracker + { + private readonly DbContext _dbContext; + + public ShardingChangeTracker(DbContext context, IStateManager stateManager, IChangeDetector changeDetector, + IModel model, IEntityEntryGraphIterator graphIterator) : base(context, stateManager, changeDetector, model, + graphIterator) + { + _dbContext = context; + } + + + public override bool HasChanges() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().Any(o => + o.Value.GetCurrentContexts().Any(r => r.Value.ChangeTracker.HasChanges())); + } + + return base.HasChanges(); + } + + public override IEnumerable Entries() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().SelectMany(o => + o.Value.GetCurrentContexts().SelectMany(cd => cd.Value.ChangeTracker.Entries())); + } + + return base.Entries(); + } + + public override IEnumerable> Entries() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().SelectMany(o => + o.Value.GetCurrentContexts().SelectMany(cd => cd.Value.ChangeTracker.Entries())); + } + + return base.Entries(); + } + + public override void DetectChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.DetectChanges()); + return; + } + base.DetectChanges(); + } + + public override void AcceptAllChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.AcceptAllChanges()); + return; + } + base.AcceptAllChanges(); + } + + private void Do(Action action) + { + var dataSourceDbContexts = ((IShardingDbContext)_dbContext).GetShardingExecutor().GetCurrentDbContexts(); + foreach (var dataSourceDbContext in dataSourceDbContexts) + { + var currentContexts = dataSourceDbContext.Value.GetCurrentContexts(); + foreach (var keyValuePair in currentContexts) + { + action(keyValuePair.Value.ChangeTracker); + } + } + } + + public override void TrackGraph(object rootEntity, Action callback) + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity); + genericDbContext.ChangeTracker.TrackGraph(rootEntity,callback); + // Do(c => c.TrackGraph(rootEntity,callback)); + } + } + + public override void TrackGraph(object rootEntity, TState state, Func, bool> callback) where TState : default + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity); + genericDbContext.ChangeTracker.TrackGraph(rootEntity,state,callback); + // Do(c => c.TrackGraph(rootEntity,callback)); + } + } + + public override void CascadeChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.CascadeChanges()); + return; + } + base.CascadeChanges(); + } + + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore3x/ShardingDbSetSource.cs b/src/ShardingCore/EFCores/EFCore3x/ShardingDbSetSource.cs new file mode 100644 index 00000000..2a289b61 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore3x/ShardingDbSetSource.cs @@ -0,0 +1,48 @@ +#if (NETCOREAPP3_0 || NETSTANDARD2_0) && SHARDINGCORE2_6 +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; +using System; +using System.Collections.Concurrent; +using System.Reflection; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: Saturday, 14 August 2021 10:17:43 + * @Email: 326308290@qq.com + */ + + public class ShardingDbSetSource:IDbSetSource + { + + private static readonly MethodInfo _genericCreateSet + = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); + + private readonly ConcurrentDictionary> _cache + = new ConcurrentDictionary>(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual object Create(DbContext context, Type type) + => CreateCore(context, type, _genericCreateSet); + + private object CreateCore(DbContext context, Type type, MethodInfo createMethod) + => _cache.GetOrAdd( + type, + t => (Func)createMethod + .MakeGenericMethod(t) + .Invoke(null, null))(context); + + private static Func CreateSetFactory() + where TEntity : class + => c => new ShardingInternalDbSet(c); + } + +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore3x/ShardingInternalDbSet.cs b/src/ShardingCore/EFCores/EFCore3x/ShardingInternalDbSet.cs new file mode 100644 index 00000000..c9489220 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore3x/ShardingInternalDbSet.cs @@ -0,0 +1,436 @@ +#if (NETCOREAPP3_0 || NETSTANDARD2_0) && SHARDINGCORE2_6 +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Internal; +using ShardingCore.Core; +using ShardingCore.Sharding.Abstractions; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Infrastructure; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Core.TrackerManagers; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualRoutes.Abstractions; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; +using ShardingCore.Utils; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/8/15 8:39:15 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + public class ShardingInternalDbSet : InternalDbSet + where TEntity : class + { + private readonly IShardingDbContext _context; + private readonly IShardingRuntimeContext _shardingRuntimeContext; + private LocalView? _localView; + + + public ShardingInternalDbSet(DbContext context) : base(context) + { + _context = (IShardingDbContext)context; + _shardingRuntimeContext = context.GetShardingRuntimeContext(); + } + + private IDataSourceRouteManager _dataSourceRouteManager; + + protected IDataSourceRouteManager DataSourceRouteManager + { + get + { + if (null == _dataSourceRouteManager) + { + _dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager(); + } + + return _dataSourceRouteManager; + } + } + + public override LocalView Local + { + get + { + + if (((DbContext)_context).ChangeTracker.AutoDetectChangesEnabled) + { + ((DbContext)_context).ChangeTracker.DetectChanges(); + } + + return _localView ??= new ShardingLocalView(this); + } + } + private ITableRouteManager _tableRouteManager; + + protected ITableRouteManager TableRouteManager + { + get + { + if (null == _tableRouteManager) + { + _tableRouteManager = _shardingRuntimeContext.GetTableRouteManager(); + } + + return _tableRouteManager; + } + } + + private IEntityMetadataManager _entityMetadataManager; + + protected IEntityMetadataManager EntityMetadataManager + { + get + { + if (null == _entityMetadataManager) + { + _entityMetadataManager = _shardingRuntimeContext.GetEntityMetadataManager(); + } + + return _entityMetadataManager; + } + } + + private ITrackerManager _trackerManager; + + protected ITrackerManager TrackerManager + { + get + { + if (null == _trackerManager) + { + _trackerManager = _shardingRuntimeContext.GetTrackerManager(); + } + + return _trackerManager; + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Add(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Add(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async ValueTask> AddAsync( + TEntity entity, + CancellationToken cancellationToken = default) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return await genericDbContext.Set().AddAsync(entity, cancellationToken); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Attach(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Attach(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Remove(TEntity entity) + { + Check.NotNull(entity, nameof(entity)); + + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Remove(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Update(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Update(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AddRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AddRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async Task AddRangeAsync(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + await aggregateKv.Key.Set().AddRangeAsync(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AttachRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AttachRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void RemoveRange(params TEntity[] entities) + { + Check.NotNull(entities, nameof(entities)); + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().RemoveRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void UpdateRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().UpdateRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AddRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AddRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async Task AddRangeAsync( + IEnumerable entities, + CancellationToken cancellationToken = default) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + await aggregateKv.Key.Set().AddRangeAsync(aggregateKv.Value, cancellationToken); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AttachRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AttachRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void RemoveRange(IEnumerable entities) + { + Check.NotNull(entities, nameof(entities)); + + + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().RemoveRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void UpdateRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().UpdateRange(aggregateKv.Value); + } + } + + private Dictionary> AggregateToDic(IEnumerable entities) + { + return entities.Select(o => + { + var dbContext = _context.CreateGenericDbContext(o); + return new + { + DbContext = dbContext, + Entity = o + }; + }).GroupBy(g => g.DbContext).ToDictionary(o => o.Key, o => o.Select(g => g.Entity)); + } + + public override TEntity Find(params object[] keyValues) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().Find(keyValues); + } + + return base.Find(keyValues); + } + + public override ValueTask FindAsync(params object[] keyValues) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().FindAsync(keyValues); + } + + return base.FindAsync(keyValues); + } + + public override ValueTask FindAsync(object[] keyValues, CancellationToken cancellationToken) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().FindAsync(keyValues, cancellationToken); + } + + return base.FindAsync(keyValues, cancellationToken); + } + + private DbContext GetDbContextByKeyValue(params object[] keyValues) + { + var entityMetadata = EntityMetadataManager.TryGet(typeof(TEntity)); + if (entityMetadata == null) + { + throw new ShardingCoreInvalidOperationException( + $"cant found type:[{typeof(TEntity)}] in {nameof(IEntityMetadataManager)}"); + } + + //既不是分表也不是分库的话就是默认对象 + if (!entityMetadata.IsShardingTable() && !entityMetadata.IsShardingDataSource()) + { + var defaultDataSourceName = _shardingRuntimeContext.GetVirtualDataSource().DefaultDataSourceName; + var routeTailFactory = _shardingRuntimeContext.GetRouteTailFactory(); + var routeTail = routeTailFactory.Create(string.Empty); + return _context.GetShareDbContext(defaultDataSourceName, routeTail); + } + + if (keyValues.Length == 1) + { + //单key字段 + if (entityMetadata.IsSingleKey) + { + var isShardingDataSource = entityMetadata.IsShardingDataSource(); + var shardingDataSourceFieldIsKey = entityMetadata.ShardingDataSourceFieldIsKey(); + if (isShardingDataSource && !shardingDataSourceFieldIsKey) + return null; + var isShardingTable = entityMetadata.IsShardingTable(); + var shardingTableFieldIsKey = entityMetadata.ShardingTableFieldIsKey(); + if (isShardingTable && !shardingTableFieldIsKey) + return null; + var primaryKeyValue = keyValues[0]; + if (primaryKeyValue != null) + { + var dataSourceName = GetDataSourceName(primaryKeyValue); + var realEntityType = TrackerManager.TranslateEntityType(typeof(TEntity)); + var tableTail = TableRouteManager.GetTableTail(dataSourceName, primaryKeyValue,realEntityType); + var routeTail = _shardingRuntimeContext.GetRouteTailFactory().Create(tableTail); + return _context.GetShareDbContext(dataSourceName, routeTail); + } + } + } + + return null; + } + + private string GetDataSourceName(object shardingKeyValue) + { + return DataSourceRouteManager.GetDataSourceName(shardingKeyValue); + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore3x/ShardingMigrator.cs b/src/ShardingCore/EFCores/EFCore3x/ShardingMigrator.cs new file mode 100644 index 00000000..f43a14c4 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore3x/ShardingMigrator.cs @@ -0,0 +1,47 @@ + +#if (NETCOREAPP3_0 || NETSTANDARD2_0)&&SHARDINGCORE2_6 +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations.Internal; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Extensions; +using ShardingCore.Helpers; + + +namespace ShardingCore.EFCores +{ + public class ShardingMigrator:Migrator + { + private readonly IShardingRuntimeContext _shardingRuntimeContext; + + + public ShardingMigrator(IShardingRuntimeContext shardingRuntimeContext, IMigrationsAssembly migrationsAssembly, IHistoryRepository historyRepository, IDatabaseCreator databaseCreator, IMigrationsSqlGenerator migrationsSqlGenerator, IRawSqlCommandBuilder rawSqlCommandBuilder, IMigrationCommandExecutor migrationCommandExecutor, IRelationalConnection connection, ISqlGenerationHelper sqlGenerationHelper, ICurrentDbContext currentContext, IDiagnosticsLogger logger, IDiagnosticsLogger commandLogger, IDatabaseProvider databaseProvider) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, currentContext, logger, commandLogger, databaseProvider) + { + _shardingRuntimeContext = shardingRuntimeContext; + } + public override void Migrate(string targetMigration = null) + { + this.MigrateAsync(targetMigration).WaitAndUnwrapException(false); + // base.Migrate(targetMigration); + } + + public override async Task MigrateAsync(string targetMigration = null, CancellationToken cancellationToken = new CancellationToken()) + { + var virtualDataSource = _shardingRuntimeContext.GetVirtualDataSource(); + var allDataSourceNames = virtualDataSource.GetAllDataSourceNames(); + await DynamicShardingHelper.DynamicMigrateWithDataSourcesAsync(_shardingRuntimeContext, allDataSourceNames, null,targetMigration,cancellationToken).ConfigureAwait(false); + + } + public override string GenerateScript(string fromMigration = null, string toMigration = null, bool idempotent = false) + { + return new ScriptMigrationGenerator(_shardingRuntimeContext, fromMigration, toMigration, idempotent).GenerateScript(); + } + } +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore3x/ShardingModelSource.cs b/src/ShardingCore/EFCores/EFCore3x/ShardingModelSource.cs index 41c7af90..3a07a231 100644 --- a/src/ShardingCore/EFCores/EFCore3x/ShardingModelSource.cs +++ b/src/ShardingCore/EFCores/EFCore3x/ShardingModelSource.cs @@ -1,4 +1,4 @@ -#if NETCOREAPP3_0 || NETSTANDARD2_0 +#if (NETCOREAPP3_0 || NETSTANDARD2_0)&&SHARDINGCORE2_6 using System; using System.Collections.Generic; using System.Linq; diff --git a/src/ShardingCore/EFCores/EFCore3x/ShardingOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore3x/ShardingOptionsExtension.cs new file mode 100644 index 00000000..5c3d0107 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore3x/ShardingOptionsExtension.cs @@ -0,0 +1,58 @@ +#if (NETCOREAPP3_0 || NETSTANDARD2_0)&&SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Core; +using ShardingCore.Core.RuntimeContexts; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + public class ShardingOptionsExtension: IDbContextOptionsExtension + { + public IShardingRuntimeContext ShardingRuntimeContext { get; } + + public ShardingOptionsExtension(IShardingRuntimeContext shardingRuntimeContext) + { + ShardingRuntimeContext = shardingRuntimeContext; + } + public void ApplyServices(IServiceCollection services) + { + services.AddSingleton(sp => ShardingRuntimeContext); + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new ShardingOptionsExtensionInfo(this); + + private class ShardingOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + private readonly ShardingOptionsExtension _shardingOptionsExtension; + public ShardingOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) + { + _shardingOptionsExtension = (ShardingOptionsExtension)extension; + } + + + public override long GetServiceProviderHashCode() => _shardingOptionsExtension.ShardingRuntimeContext.GetHashCode(); + + public override void PopulateDebugInfo(IDictionary debugInfo) { } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "ShardingOptionsExtension"; + } + } +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore3x/ShardingWrapOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore3x/ShardingWrapOptionsExtension.cs new file mode 100644 index 00000000..afc92e05 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore3x/ShardingWrapOptionsExtension.cs @@ -0,0 +1,46 @@ +#if (NETCOREAPP3_0 || NETSTANDARD2_0) && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Core; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + public class ShardingWrapOptionsExtension: IDbContextOptionsExtension + { + public void ApplyServices(IServiceCollection services) + { + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new ShardingWrapDbContextOptionsExtensionInfo(this); + + private class ShardingWrapDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + public ShardingWrapDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) { } + + public override long GetServiceProviderHashCode() => 0; + + public override void PopulateDebugInfo(IDictionary debugInfo) { } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "ShardingWrapOptionsExtension"; + } + } + +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore3x/Tx/ShardingRelationalTransaction.cs b/src/ShardingCore/EFCores/EFCore3x/Tx/ShardingRelationalTransaction.cs new file mode 100644 index 00000000..fd9af368 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore3x/Tx/ShardingRelationalTransaction.cs @@ -0,0 +1,74 @@ +#if (NETCOREAPP3_0 || NETSTANDARD2_0)&&SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Exceptions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/9/5 20:37:36 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class ShardingRelationalTransaction : RelationalTransaction + { + private readonly IShardingDbContext _shardingDbContext; + private readonly IShardingDbContextExecutor _shardingDbContextExecutor; + public ShardingRelationalTransaction(IShardingDbContext shardingDbContext, IRelationalConnection connection, + DbTransaction transaction, Guid transactionId, + IDiagnosticsLogger logger, bool transactionOwned) : base(connection, + transaction, transactionId, logger, transactionOwned) + { + _shardingDbContext = shardingDbContext ?? + throw new ShardingCoreInvalidOperationException( + $"should implement {nameof(IShardingDbContext)}"); + _shardingDbContextExecutor = shardingDbContext.GetShardingExecutor() ?? + throw new ShardingCoreInvalidOperationException( + $"{shardingDbContext.GetType()} cant get {nameof(IShardingDbContextExecutor)} from {nameof(shardingDbContext.GetShardingExecutor)}"); + } + + public override void Commit() + { + base.Commit(); + _shardingDbContextExecutor.Commit(); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + + public override void Rollback() + { + base.Rollback(); + _shardingDbContextExecutor.Rollback(); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + +#if !NETCOREAPP2_0 + public override async Task RollbackAsync(CancellationToken cancellationToken = new CancellationToken()) + { + await base.RollbackAsync(cancellationToken); + + await _shardingDbContextExecutor.RollbackAsync(cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + + public override async Task CommitAsync(CancellationToken cancellationToken = new CancellationToken()) + { + await base.CommitAsync(cancellationToken); + + await _shardingDbContextExecutor.CommitAsync(cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } +#endif + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore3x/Tx/ShardingRelationalTransactionFactory.cs b/src/ShardingCore/EFCores/EFCore3x/Tx/ShardingRelationalTransactionFactory.cs new file mode 100644 index 00000000..b4d6c20d --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore3x/Tx/ShardingRelationalTransactionFactory.cs @@ -0,0 +1,39 @@ +#if (NETCOREAPP3_0 || NETSTANDARD2_0) && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Extensions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/9/5 16:03:04 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + public class ShardingRelationalTransactionFactory : RelationalTransactionFactory where TShardingDbContext : DbContext, IShardingDbContext + { + private readonly RelationalTransactionFactoryDependencies _dependencies; + public ShardingRelationalTransactionFactory(RelationalTransactionFactoryDependencies dependencies) : base(dependencies) + { + _dependencies = dependencies; + } + public override RelationalTransaction Create(IRelationalConnection connection, DbTransaction transaction, Guid transactionId, + IDiagnosticsLogger logger, bool transactionOwned) + { + var shardingDbContext = connection.Context as IShardingDbContext; + return new ShardingRelationalTransaction(shardingDbContext, connection, transaction, transactionId, logger, transactionOwned); + } + } + +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore3x/Tx/ShardingRelationalTransactionManager.cs b/src/ShardingCore/EFCores/EFCore3x/Tx/ShardingRelationalTransactionManager.cs new file mode 100644 index 00000000..805df3b6 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore3x/Tx/ShardingRelationalTransactionManager.cs @@ -0,0 +1,103 @@ +#if (NETCOREAPP3_0 || NETSTANDARD2_0) && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/20 10:08:42 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + /// + /// manage transaction + /// + public class ShardingRelationalTransactionManager : IRelationalTransactionManager where TShardingDbContext : DbContext, IShardingDbContext + { + private readonly IRelationalConnection _relationalConnection; + private readonly IShardingDbContext _shardingDbContext; + private readonly IShardingDbContextExecutor _shardingDbContextExecutor; + + public ShardingRelationalTransactionManager(IRelationalConnection relationalConnection) + { + _relationalConnection = relationalConnection; + _shardingDbContext = relationalConnection.Context as IShardingDbContext??throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); + _shardingDbContextExecutor = _shardingDbContext.GetShardingExecutor(); + } + + public void ResetState() + { + _relationalConnection.ResetState(); + } + + public IDbContextTransaction BeginTransaction() + { + return BeginTransaction(IsolationLevel.Unspecified); + } + + public Task BeginTransactionAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return BeginTransactionAsync(IsolationLevel.Unspecified, cancellationToken); + } + + public void CommitTransaction() + { + _relationalConnection.CommitTransaction(); + } + + + public void RollbackTransaction() + { + _relationalConnection.RollbackTransaction(); + } + + + public IDbContextTransaction CurrentTransaction => _relationalConnection.CurrentTransaction; + public IDbContextTransaction BeginTransaction(IsolationLevel isolationLevel) + { + var dbContextTransaction = _relationalConnection.BeginTransaction(isolationLevel); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public async Task BeginTransactionAsync(IsolationLevel isolationLevel, + CancellationToken cancellationToken = new CancellationToken()) + { + var dbContextTransaction = await _relationalConnection.BeginTransactionAsync(isolationLevel, cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public IDbContextTransaction UseTransaction(DbTransaction transaction) + { + var dbContextTransaction = _relationalConnection.UseTransaction(transaction); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public Task ResetStateAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return _relationalConnection.ResetStateAsync(cancellationToken); + } + + public async Task UseTransactionAsync(DbTransaction transaction, CancellationToken cancellationToken = new CancellationToken()) + { + var dbContextTransaction = await _relationalConnection.UseTransactionAsync(transaction, cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + } +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore3x/UnionAllMergeOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore3x/UnionAllMergeOptionsExtension.cs new file mode 100644 index 00000000..78ee1299 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore3x/UnionAllMergeOptionsExtension.cs @@ -0,0 +1,43 @@ +#if (NETCOREAPP3_0 || NETSTANDARD2_0) && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class UnionAllMergeOptionsExtension: IDbContextOptionsExtension + { + public void ApplyServices(IServiceCollection services) + { + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new UnionAllMergeDbContextOptionsExtensionInfo(this); + + private class UnionAllMergeDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + public UnionAllMergeDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) { } + + public override long GetServiceProviderHashCode() => 0; + + public override void PopulateDebugInfo(IDictionary debugInfo) { } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "UnionAllMergeOptionsExtension"; + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore5x/ScriptMigrationGenerator.cs b/src/ShardingCore/EFCores/EFCore5x/ScriptMigrationGenerator.cs index f66d964b..7e553f64 100644 --- a/src/ShardingCore/EFCores/EFCore5x/ScriptMigrationGenerator.cs +++ b/src/ShardingCore/EFCores/EFCore5x/ScriptMigrationGenerator.cs @@ -1,4 +1,4 @@ -#if NET5_0 || NETSTANDARD2_1 +#if (NET5_0 || NETSTANDARD2_1)&&SHARDINGCORE2_6 using Microsoft.EntityFrameworkCore.Migrations; using ShardingCore.Core.RuntimeContexts; diff --git a/src/ShardingCore/EFCores/ChangeTrackers/ShardingChangeTracker.cs b/src/ShardingCore/EFCores/EFCore5x/ShardingChangeTracker.cs similarity index 85% rename from src/ShardingCore/EFCores/ChangeTrackers/ShardingChangeTracker.cs rename to src/ShardingCore/EFCores/EFCore5x/ShardingChangeTracker.cs index 4e61c171..52451c4c 100644 --- a/src/ShardingCore/EFCores/ChangeTrackers/ShardingChangeTracker.cs +++ b/src/ShardingCore/EFCores/EFCore5x/ShardingChangeTracker.cs @@ -1,4 +1,5 @@ -using System; +#if (NET5_0 || NETSTANDARD2_1)&&SHARDINGCORE2_6 +using System; using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; @@ -7,7 +8,7 @@ using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; using Microsoft.EntityFrameworkCore.Metadata; using ShardingCore.Sharding.Abstractions; -namespace ShardingCore.EFCores.ChangeTrackers +namespace ShardingCore.EFCores { public class ShardingChangeTracker : ChangeTracker { @@ -20,10 +21,6 @@ namespace ShardingCore.EFCores.ChangeTrackers _dbContext = context; } -#if !NETCOREAPP2_0 && !NETSTANDARD2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_1 && !NET5_0 && !NET6_0 - error -#endif - public override bool HasChanges() { if (_dbContext is IShardingDbContext shardingDbContext) @@ -100,7 +97,6 @@ namespace ShardingCore.EFCores.ChangeTrackers } } -#if !NETCOREAPP2_0 public override void TrackGraph(object rootEntity, TState state, Func, bool> callback) where TState : default { if (_dbContext is IShardingDbContext shardingDbContext) @@ -121,8 +117,6 @@ namespace ShardingCore.EFCores.ChangeTrackers base.CascadeChanges(); } -#endif -#if !NETCOREAPP2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_0 public override void Clear() { if (_dbContext is IShardingDbContext) @@ -132,18 +126,7 @@ namespace ShardingCore.EFCores.ChangeTrackers } base.Clear(); } -#endif -#if NETCOREAPP2_0 - public override void TrackGraph(object rootEntity, TState state, Func callback) - { - if (_dbContext is IShardingDbContext shardingDbContext) - { - var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity); - genericDbContext.ChangeTracker.TrackGraph(rootEntity,state,callback); - // Do(c => c.TrackGraph(rootEntity,callback)); - } - } -#endif } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore5x/ShardingDbSetSource.cs b/src/ShardingCore/EFCores/EFCore5x/ShardingDbSetSource.cs new file mode 100644 index 00000000..7f0e9f5d --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore5x/ShardingDbSetSource.cs @@ -0,0 +1,57 @@ +#if (NET5_0 || NETSTANDARD2_1 ) && SHARDINGCORE2_6 +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; +using System; +using System.Collections.Concurrent; +using System.Reflection; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: Saturday, 14 August 2021 10:17:43 + * @Email: 326308290@qq.com + */ + + public class ShardingDbSetSource : IDbSetSource + { + + private static readonly MethodInfo _genericCreateSet + = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); + + private readonly ConcurrentDictionary<(Type Type, string Name), Func> _cache + = new ConcurrentDictionary<(Type Type, string Name), Func>(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual object Create(DbContext context, Type type) + => CreateCore(context, type, null, _genericCreateSet); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual object Create(DbContext context, string name, Type type) + => CreateCore(context, type, name, _genericCreateSet); + + private object CreateCore(DbContext context, Type type, string name, MethodInfo createMethod) + => _cache.GetOrAdd( + (type, name), + t => (Func)createMethod + .MakeGenericMethod(t.Type) + .Invoke(null, null))(context, name); + + private static Func CreateSetFactory() + where TEntity : class + => (c, name) => new ShardingInternalDbSet(c, name); + } + +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore5x/ShardingInternalDbSet.cs b/src/ShardingCore/EFCores/EFCore5x/ShardingInternalDbSet.cs new file mode 100644 index 00000000..d75780b7 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore5x/ShardingInternalDbSet.cs @@ -0,0 +1,439 @@ +#if (NET5_0 || NETSTANDARD2_1 ) && SHARDINGCORE2_6 +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Internal; +using ShardingCore.Core; +using ShardingCore.Sharding.Abstractions; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Infrastructure; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Core.TrackerManagers; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualRoutes.Abstractions; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; +using ShardingCore.Utils; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/8/15 8:39:15 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + public class ShardingInternalDbSet : InternalDbSet + where TEntity : class + { + private readonly IShardingDbContext _context; + private readonly IShardingRuntimeContext _shardingRuntimeContext; + private LocalView? _localView; + + + + public ShardingInternalDbSet(DbContext context, string entityTypeName) : base(context, entityTypeName) + { + _context = (IShardingDbContext)context; + _shardingRuntimeContext = context.GetShardingRuntimeContext(); + } + + private IDataSourceRouteManager _dataSourceRouteManager; + + protected IDataSourceRouteManager DataSourceRouteManager + { + get + { + if (null == _dataSourceRouteManager) + { + _dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager(); + } + + return _dataSourceRouteManager; + } + } + + public override LocalView Local + { + get + { + + if (((DbContext)_context).ChangeTracker.AutoDetectChangesEnabled) + { + ((DbContext)_context).ChangeTracker.DetectChanges(); + } + + return _localView ??= new ShardingLocalView(this); + } + } + private ITableRouteManager _tableRouteManager; + + protected ITableRouteManager TableRouteManager + { + get + { + if (null == _tableRouteManager) + { + _tableRouteManager = _shardingRuntimeContext.GetTableRouteManager(); + } + + return _tableRouteManager; + } + } + + private IEntityMetadataManager _entityMetadataManager; + + protected IEntityMetadataManager EntityMetadataManager + { + get + { + if (null == _entityMetadataManager) + { + _entityMetadataManager = _shardingRuntimeContext.GetEntityMetadataManager(); + } + + return _entityMetadataManager; + } + } + + private ITrackerManager _trackerManager; + + protected ITrackerManager TrackerManager + { + get + { + if (null == _trackerManager) + { + _trackerManager = _shardingRuntimeContext.GetTrackerManager(); + } + + return _trackerManager; + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Add(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Add(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async ValueTask> AddAsync( + TEntity entity, + CancellationToken cancellationToken = default) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return await genericDbContext.Set().AddAsync(entity, cancellationToken); + } + + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Attach(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Attach(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Remove(TEntity entity) + { + Check.NotNull(entity, nameof(entity)); + + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Remove(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Update(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Update(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AddRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AddRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async Task AddRangeAsync(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + await aggregateKv.Key.Set().AddRangeAsync(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AttachRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AttachRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void RemoveRange(params TEntity[] entities) + { + Check.NotNull(entities, nameof(entities)); + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().RemoveRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void UpdateRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().UpdateRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AddRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AddRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async Task AddRangeAsync( + IEnumerable entities, + CancellationToken cancellationToken = default) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + await aggregateKv.Key.Set().AddRangeAsync(aggregateKv.Value, cancellationToken); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AttachRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AttachRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void RemoveRange(IEnumerable entities) + { + Check.NotNull(entities, nameof(entities)); + + + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().RemoveRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void UpdateRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().UpdateRange(aggregateKv.Value); + } + } + + private Dictionary> AggregateToDic(IEnumerable entities) + { + return entities.Select(o => + { + var dbContext = _context.CreateGenericDbContext(o); + return new + { + DbContext = dbContext, + Entity = o + }; + }).GroupBy(g => g.DbContext).ToDictionary(o => o.Key, o => o.Select(g => g.Entity)); + } + + public override TEntity Find(params object[] keyValues) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().Find(keyValues); + } + + return base.Find(keyValues); + } + + public override ValueTask FindAsync(params object[] keyValues) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().FindAsync(keyValues); + } + + return base.FindAsync(keyValues); + } + + public override ValueTask FindAsync(object[] keyValues, CancellationToken cancellationToken) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().FindAsync(keyValues, cancellationToken); + } + + return base.FindAsync(keyValues, cancellationToken); + } + + + private DbContext GetDbContextByKeyValue(params object[] keyValues) + { + var entityMetadata = EntityMetadataManager.TryGet(typeof(TEntity)); + if (entityMetadata == null) + { + throw new ShardingCoreInvalidOperationException( + $"cant found type:[{typeof(TEntity)}] in {nameof(IEntityMetadataManager)}"); + } + + //既不是分表也不是分库的话就是默认对象 + if (!entityMetadata.IsShardingTable() && !entityMetadata.IsShardingDataSource()) + { + var defaultDataSourceName = _shardingRuntimeContext.GetVirtualDataSource().DefaultDataSourceName; + var routeTailFactory = _shardingRuntimeContext.GetRouteTailFactory(); + var routeTail = routeTailFactory.Create(string.Empty); + return _context.GetShareDbContext(defaultDataSourceName, routeTail); + } + + if (keyValues.Length == 1) + { + //单key字段 + if (entityMetadata.IsSingleKey) + { + var isShardingDataSource = entityMetadata.IsShardingDataSource(); + var shardingDataSourceFieldIsKey = entityMetadata.ShardingDataSourceFieldIsKey(); + if (isShardingDataSource && !shardingDataSourceFieldIsKey) + return null; + var isShardingTable = entityMetadata.IsShardingTable(); + var shardingTableFieldIsKey = entityMetadata.ShardingTableFieldIsKey(); + if (isShardingTable && !shardingTableFieldIsKey) + return null; + var primaryKeyValue = keyValues[0]; + if (primaryKeyValue != null) + { + var dataSourceName = GetDataSourceName(primaryKeyValue); + var realEntityType = TrackerManager.TranslateEntityType(typeof(TEntity)); + var tableTail = TableRouteManager.GetTableTail(dataSourceName, primaryKeyValue,realEntityType); + var routeTail = _shardingRuntimeContext.GetRouteTailFactory().Create(tableTail); + return _context.GetShareDbContext(dataSourceName, routeTail); + } + } + } + + return null; + } + + private string GetDataSourceName(object shardingKeyValue) + { + return DataSourceRouteManager.GetDataSourceName(shardingKeyValue); + } + } +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore5x/ShardingMigrator.cs b/src/ShardingCore/EFCores/EFCore5x/ShardingMigrator.cs new file mode 100644 index 00000000..c2d297cc --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore5x/ShardingMigrator.cs @@ -0,0 +1,50 @@ + +#if (NET5_0 || NETSTANDARD2_1)&&SHARDINGCORE2_6 +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations.Internal; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Extensions; +using ShardingCore.Helpers; +using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; + +namespace ShardingCore.EFCores +{ + public class ShardingMigrator:Migrator + { + private readonly IShardingRuntimeContext _shardingRuntimeContext; + + + public ShardingMigrator(IShardingRuntimeContext shardingRuntimeContext, IMigrationsAssembly migrationsAssembly, IHistoryRepository historyRepository, IDatabaseCreator databaseCreator, IMigrationsSqlGenerator migrationsSqlGenerator, IRawSqlCommandBuilder rawSqlCommandBuilder, IMigrationCommandExecutor migrationCommandExecutor, IRelationalConnection connection, ISqlGenerationHelper sqlGenerationHelper, ICurrentDbContext currentContext, IConventionSetBuilder conventionSetBuilder, IDiagnosticsLogger logger, IDiagnosticsLogger commandLogger, IDatabaseProvider databaseProvider) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, currentContext, conventionSetBuilder, logger, commandLogger, databaseProvider) + { + _shardingRuntimeContext = shardingRuntimeContext; + } + public override void Migrate(string targetMigration = null) + { + this.MigrateAsync(targetMigration).WaitAndUnwrapException(false); + // base.Migrate(targetMigration); + } + + public override async Task MigrateAsync(string targetMigration = null, CancellationToken cancellationToken = new CancellationToken()) + { + var virtualDataSource = _shardingRuntimeContext.GetVirtualDataSource(); + var allDataSourceNames = virtualDataSource.GetAllDataSourceNames(); + await DynamicShardingHelper.DynamicMigrateWithDataSourcesAsync(_shardingRuntimeContext, allDataSourceNames, null,targetMigration,cancellationToken).ConfigureAwait(false); + + } + + public override string GenerateScript(string fromMigration = null, string toMigration = null, + MigrationsSqlGenerationOptions options = MigrationsSqlGenerationOptions.Default) + { + return new ScriptMigrationGenerator(_shardingRuntimeContext, fromMigration, toMigration, options).GenerateScript(); + } + } + +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore5x/ShardingModelSource.cs b/src/ShardingCore/EFCores/EFCore5x/ShardingModelSource.cs index d2243a14..2afed500 100644 --- a/src/ShardingCore/EFCores/EFCore5x/ShardingModelSource.cs +++ b/src/ShardingCore/EFCores/EFCore5x/ShardingModelSource.cs @@ -1,4 +1,4 @@ -#if NET5_0 || NETSTANDARD2_1 +#if (NET5_0 || NETSTANDARD2_1)&&SHARDINGCORE2_6 using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/src/ShardingCore/EFCores/EFCore5x/ShardingOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore5x/ShardingOptionsExtension.cs new file mode 100644 index 00000000..a6e984fb --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore5x/ShardingOptionsExtension.cs @@ -0,0 +1,58 @@ +#if (NET5_0 || NETSTANDARD2_1) && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Core; +using ShardingCore.Core.RuntimeContexts; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + public class ShardingOptionsExtension: IDbContextOptionsExtension + { + public IShardingRuntimeContext ShardingRuntimeContext { get; } + + public ShardingOptionsExtension(IShardingRuntimeContext shardingRuntimeContext) + { + ShardingRuntimeContext = shardingRuntimeContext; + } + public void ApplyServices(IServiceCollection services) + { + services.AddSingleton(sp => ShardingRuntimeContext); + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new ShardingOptionsExtensionInfo(this); + + private class ShardingOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + private readonly ShardingOptionsExtension _shardingOptionsExtension; + public ShardingOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) + { + _shardingOptionsExtension = (ShardingOptionsExtension)extension; + } + + + public override long GetServiceProviderHashCode() => _shardingOptionsExtension.ShardingRuntimeContext.GetHashCode(); + + public override void PopulateDebugInfo(IDictionary debugInfo) { } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "ShardingOptionsExtension"; + } + } +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore5x/ShardingWrapOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore5x/ShardingWrapOptionsExtension.cs new file mode 100644 index 00000000..193cb799 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore5x/ShardingWrapOptionsExtension.cs @@ -0,0 +1,48 @@ +#if (NET5_0 || NETSTANDARD2_1) && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Core; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + + + public class ShardingWrapOptionsExtension: IDbContextOptionsExtension + { + public void ApplyServices(IServiceCollection services) + { + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new ShardingWrapDbContextOptionsExtensionInfo(this); + + private class ShardingWrapDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + public ShardingWrapDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) { } + + public override long GetServiceProviderHashCode() => 0; + + public override void PopulateDebugInfo(IDictionary debugInfo) { } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "ShardingWrapOptionsExtension"; + } + } +} + +#endif diff --git a/src/ShardingCore/EFCores/RelationTransactions/ShardingRelationalTransaction.cs b/src/ShardingCore/EFCores/EFCore5x/Tx/ShardingRelationalTransaction.cs similarity index 69% rename from src/ShardingCore/EFCores/RelationTransactions/ShardingRelationalTransaction.cs rename to src/ShardingCore/EFCores/EFCore5x/Tx/ShardingRelationalTransaction.cs index 0cfc15c7..828ab689 100644 --- a/src/ShardingCore/EFCores/RelationTransactions/ShardingRelationalTransaction.cs +++ b/src/ShardingCore/EFCores/EFCore5x/Tx/ShardingRelationalTransaction.cs @@ -1,4 +1,5 @@ -using System; +#if (NET5_0 || NETSTANDARD2_1)&&SHARDINGCORE2_6 +using System; using System.Collections.Generic; using System.Data.Common; using System.Text; @@ -20,26 +21,11 @@ namespace ShardingCore.EFCores * @Ver: 1.0 * @Email: 326308290@qq.com */ -#if !NETCOREAPP2_0 && !NETSTANDARD2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_1 && !NET5_0 && !NET6_0 - error -#endif public class ShardingRelationalTransaction : RelationalTransaction { private readonly IShardingDbContext _shardingDbContext; private readonly IShardingDbContextExecutor _shardingDbContextExecutor; -#if NET6_0 - public ShardingRelationalTransaction(IShardingDbContext shardingDbContext, IRelationalConnection connection, DbTransaction transaction, Guid transactionId, IDiagnosticsLogger logger, bool transactionOwned, ISqlGenerationHelper sqlGenerationHelper) : base(connection, transaction, transactionId, logger, transactionOwned, sqlGenerationHelper) - { - _shardingDbContext = - shardingDbContext ?? throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); - _shardingDbContextExecutor = shardingDbContext.GetShardingExecutor() ?? - throw new ShardingCoreInvalidOperationException( - $"{shardingDbContext.GetType()} cant get {nameof(IShardingDbContextExecutor)} from {nameof(shardingDbContext.GetShardingExecutor)}"); - } - -#endif -#if NETCOREAPP3_0 || NETSTANDARD2_0 || NET5_0 || NETSTANDARD2_1 public ShardingRelationalTransaction(IShardingDbContext shardingDbContext, IRelationalConnection connection, DbTransaction transaction, Guid transactionId, IDiagnosticsLogger logger, bool transactionOwned) : base(connection, @@ -53,19 +39,6 @@ namespace ShardingCore.EFCores $"{shardingDbContext.GetType()} cant get {nameof(IShardingDbContextExecutor)} from {nameof(shardingDbContext.GetShardingExecutor)}"); } -#endif -#if NETCOREAPP2_0 - public ShardingRelationalTransaction(IShardingDbContext shardingDbContext, IRelationalConnection connection, DbTransaction transaction,IDiagnosticsLogger logger, bool transactionOwned) : base(connection, transaction, logger, transactionOwned) - { - _shardingDbContext = - shardingDbContext??throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); - _shardingDbContextExecutor = shardingDbContext.GetShardingExecutor() ?? - throw new ShardingCoreInvalidOperationException( - $"{shardingDbContext.GetType()} cant get {nameof(IShardingDbContextExecutor)} from {nameof(shardingDbContext.GetShardingExecutor)}"); - - } - -#endif //protected override void ClearTransaction() //{ // if (_canClear) @@ -88,7 +61,6 @@ namespace ShardingCore.EFCores _shardingDbContextExecutor.NotifyShardingTransaction(); } -#if !NETCOREAPP2_0 public override async Task RollbackAsync(CancellationToken cancellationToken = new CancellationToken()) { await base.RollbackAsync(cancellationToken); @@ -104,7 +76,6 @@ namespace ShardingCore.EFCores await _shardingDbContextExecutor.CommitAsync(cancellationToken); _shardingDbContextExecutor.NotifyShardingTransaction(); } -#if !NETCOREAPP3_0&&!NETSTANDARD2_0 public override void CreateSavepoint(string name) { base.CreateSavepoint(name); @@ -140,7 +111,6 @@ namespace ShardingCore.EFCores await base.ReleaseSavepointAsync(name, cancellationToken); await _shardingDbContextExecutor.ReleaseSavepointAsync(name,cancellationToken); } -#endif -#endif } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore5x/Tx/ShardingRelationalTransactionFactory.cs b/src/ShardingCore/EFCores/EFCore5x/Tx/ShardingRelationalTransactionFactory.cs new file mode 100644 index 00000000..3a52e2f0 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore5x/Tx/ShardingRelationalTransactionFactory.cs @@ -0,0 +1,39 @@ +#if (NET5_0 || NETSTANDARD2_1) && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Extensions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/9/5 16:03:04 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + + public class ShardingRelationalTransactionFactory : RelationalTransactionFactory where TShardingDbContext : DbContext, IShardingDbContext + { + private readonly RelationalTransactionFactoryDependencies _dependencies; + public ShardingRelationalTransactionFactory(RelationalTransactionFactoryDependencies dependencies) : base(dependencies) + { + _dependencies = dependencies; + } + public override RelationalTransaction Create(IRelationalConnection connection, DbTransaction transaction, Guid transactionId, + IDiagnosticsLogger logger, bool transactionOwned) + { + var shardingDbContext = connection.Context as IShardingDbContext; + return new ShardingRelationalTransaction(shardingDbContext, connection, transaction, transactionId, logger, transactionOwned); + } + } +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore5x/Tx/ShardingRelationalTransactionManager.cs b/src/ShardingCore/EFCores/EFCore5x/Tx/ShardingRelationalTransactionManager.cs new file mode 100644 index 00000000..64481077 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore5x/Tx/ShardingRelationalTransactionManager.cs @@ -0,0 +1,125 @@ +#if (NET5_0 || NETSTANDARD2_1) && SHARDINGCORE2_6 + +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/20 10:08:42 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + /// + /// manage transaction + /// + public class ShardingRelationalTransactionManager : IRelationalTransactionManager where TShardingDbContext : DbContext, IShardingDbContext + { + private readonly IRelationalConnection _relationalConnection; + private readonly IShardingDbContext _shardingDbContext; + private readonly IShardingDbContextExecutor _shardingDbContextExecutor; + public ShardingRelationalTransactionManager(IRelationalConnection relationalConnection) + { + _relationalConnection = relationalConnection; + _shardingDbContext = relationalConnection.Context as IShardingDbContext??throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); + _shardingDbContextExecutor = _shardingDbContext.GetShardingExecutor(); + } + + public void ResetState() + { + _relationalConnection.ResetState(); + } + + public IDbContextTransaction BeginTransaction() + { + return BeginTransaction(IsolationLevel.Unspecified); + } + + public Task BeginTransactionAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return BeginTransactionAsync(IsolationLevel.Unspecified, cancellationToken); + } + + public void CommitTransaction() + { + _relationalConnection.CommitTransaction(); + } + + + public void RollbackTransaction() + { + _relationalConnection.RollbackTransaction(); + } + + + public IDbContextTransaction CurrentTransaction => _relationalConnection.CurrentTransaction; + public IDbContextTransaction BeginTransaction(IsolationLevel isolationLevel) + { + var dbContextTransaction = _relationalConnection.BeginTransaction(isolationLevel); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public async Task BeginTransactionAsync(IsolationLevel isolationLevel, + CancellationToken cancellationToken = new CancellationToken()) + { + var dbContextTransaction = await _relationalConnection.BeginTransactionAsync(isolationLevel, cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public IDbContextTransaction UseTransaction(DbTransaction transaction) + { + var dbContextTransaction = _relationalConnection.UseTransaction(transaction); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public Task ResetStateAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return _relationalConnection.ResetStateAsync(cancellationToken); + } + + public async Task UseTransactionAsync(DbTransaction transaction, CancellationToken cancellationToken = new CancellationToken()) + { + var dbContextTransaction = await _relationalConnection.UseTransactionAsync(transaction, cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public Task CommitTransactionAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return _relationalConnection.CommitTransactionAsync(cancellationToken); + } + public Task RollbackTransactionAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return _relationalConnection.RollbackTransactionAsync(cancellationToken); + } + public IDbContextTransaction UseTransaction(DbTransaction transaction, Guid transactionId) + { + var dbContextTransaction = _relationalConnection.UseTransaction(transaction, transactionId); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + public async Task UseTransactionAsync(DbTransaction transaction, Guid transactionId, + CancellationToken cancellationToken = new CancellationToken()) + { + var dbContextTransaction = await _relationalConnection.UseTransactionAsync(transaction, transactionId, cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore5x/UnionAllMergeOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore5x/UnionAllMergeOptionsExtension.cs new file mode 100644 index 00000000..f48e0902 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore5x/UnionAllMergeOptionsExtension.cs @@ -0,0 +1,47 @@ +#if (NET5_0 || NETSTANDARD2_1) && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + + + public class UnionAllMergeOptionsExtension: IDbContextOptionsExtension + { + public void ApplyServices(IServiceCollection services) + { + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new UnionAllMergeDbContextOptionsExtensionInfo(this); + + private class UnionAllMergeDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + public UnionAllMergeDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) { } + + public override long GetServiceProviderHashCode() => 0; + + public override void PopulateDebugInfo(IDictionary debugInfo) { } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "UnionAllMergeOptionsExtension"; + } + } +} + +#endif diff --git a/src/ShardingCore/EFCores/EFCore6x/ScriptMigrationGenerator.cs b/src/ShardingCore/EFCores/EFCore6x/ScriptMigrationGenerator.cs index 3f556d01..3412d11a 100644 --- a/src/ShardingCore/EFCores/EFCore6x/ScriptMigrationGenerator.cs +++ b/src/ShardingCore/EFCores/EFCore6x/ScriptMigrationGenerator.cs @@ -1,4 +1,4 @@ -#if NET6_0 +#if NET6_0&&SHARDINGCORE2_6 using Microsoft.EntityFrameworkCore.Migrations; using ShardingCore.Core.RuntimeContexts; diff --git a/src/ShardingCore/EFCores/EFCore6x/ShardingChangeTracker.cs b/src/ShardingCore/EFCores/EFCore6x/ShardingChangeTracker.cs new file mode 100644 index 00000000..80be06b1 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore6x/ShardingChangeTracker.cs @@ -0,0 +1,132 @@ +#if NET6_0&&SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +using Microsoft.EntityFrameworkCore.Metadata; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + public class ShardingChangeTracker : ChangeTracker + { + private readonly DbContext _dbContext; + + public ShardingChangeTracker(DbContext context, IStateManager stateManager, IChangeDetector changeDetector, + IModel model, IEntityEntryGraphIterator graphIterator) : base(context, stateManager, changeDetector, model, + graphIterator) + { + _dbContext = context; + } + + public override bool HasChanges() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().Any(o => + o.Value.GetCurrentContexts().Any(r => r.Value.ChangeTracker.HasChanges())); + } + + return base.HasChanges(); + } + + public override IEnumerable Entries() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().SelectMany(o => + o.Value.GetCurrentContexts().SelectMany(cd => cd.Value.ChangeTracker.Entries())); + } + + return base.Entries(); + } + + public override IEnumerable> Entries() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().SelectMany(o => + o.Value.GetCurrentContexts().SelectMany(cd => cd.Value.ChangeTracker.Entries())); + } + + return base.Entries(); + } + + public override void DetectChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.DetectChanges()); + return; + } + base.DetectChanges(); + } + + public override void AcceptAllChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.AcceptAllChanges()); + return; + } + base.AcceptAllChanges(); + } + + private void Do(Action action) + { + var dataSourceDbContexts = ((IShardingDbContext)_dbContext).GetShardingExecutor().GetCurrentDbContexts(); + foreach (var dataSourceDbContext in dataSourceDbContexts) + { + var currentContexts = dataSourceDbContext.Value.GetCurrentContexts(); + foreach (var keyValuePair in currentContexts) + { + action(keyValuePair.Value.ChangeTracker); + } + } + } + + public override void TrackGraph(object rootEntity, Action callback) + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity); + genericDbContext.ChangeTracker.TrackGraph(rootEntity,callback); + // Do(c => c.TrackGraph(rootEntity,callback)); + } + } + + public override void TrackGraph(object rootEntity, TState state, Func, bool> callback) where TState : default + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity); + genericDbContext.ChangeTracker.TrackGraph(rootEntity,state,callback); + // Do(c => c.TrackGraph(rootEntity,callback)); + } + } + + public override void CascadeChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.CascadeChanges()); + return; + } + base.CascadeChanges(); + } + + public override void Clear() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.Clear()); + return; + } + base.Clear(); + } + + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore6x/ShardingDbSetSource.cs b/src/ShardingCore/EFCores/EFCore6x/ShardingDbSetSource.cs new file mode 100644 index 00000000..96c40d89 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore6x/ShardingDbSetSource.cs @@ -0,0 +1,57 @@ +#if NET6_0 && SHARDINGCORE2_6 +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; +using System; +using System.Collections.Concurrent; +using System.Reflection; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: Saturday, 14 August 2021 10:17:43 + * @Email: 326308290@qq.com + */ + + public class ShardingDbSetSource : IDbSetSource + { + + private static readonly MethodInfo _genericCreateSet + = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); + + private readonly ConcurrentDictionary<(Type Type, string Name), Func> _cache + = new ConcurrentDictionary<(Type Type, string Name), Func>(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual object Create(DbContext context, Type type) + => CreateCore(context, type, null, _genericCreateSet); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual object Create(DbContext context, string name, Type type) + => CreateCore(context, type, name, _genericCreateSet); + + private object CreateCore(DbContext context, Type type, string name, MethodInfo createMethod) + => _cache.GetOrAdd( + (type, name), + t => (Func)createMethod + .MakeGenericMethod(t.Type) + .Invoke(null, null))(context, name); + + private static Func CreateSetFactory() + where TEntity : class + => (c, name) => new ShardingInternalDbSet(c, name); + } + +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore6x/ShardingInternalDbSet.cs b/src/ShardingCore/EFCores/EFCore6x/ShardingInternalDbSet.cs new file mode 100644 index 00000000..6b227177 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore6x/ShardingInternalDbSet.cs @@ -0,0 +1,436 @@ +#if NET6_0 && SHARDINGCORE2_6 +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Internal; +using ShardingCore.Core; +using ShardingCore.Sharding.Abstractions; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Infrastructure; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Core.TrackerManagers; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualRoutes.Abstractions; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; +using ShardingCore.Utils; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/8/15 8:39:15 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class ShardingInternalDbSet : InternalDbSet + where TEntity : class + { + private readonly IShardingDbContext _context; + private readonly IShardingRuntimeContext _shardingRuntimeContext; + private LocalView? _localView; + + + + public ShardingInternalDbSet(DbContext context, string entityTypeName) : base(context, entityTypeName) + { + _context = (IShardingDbContext)context; + _shardingRuntimeContext = context.GetShardingRuntimeContext(); + } + + private IDataSourceRouteManager _dataSourceRouteManager; + + protected IDataSourceRouteManager DataSourceRouteManager + { + get + { + if (null == _dataSourceRouteManager) + { + _dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager(); + } + + return _dataSourceRouteManager; + } + } + + public override LocalView Local + { + get + { + + if (((DbContext)_context).ChangeTracker.AutoDetectChangesEnabled) + { + ((DbContext)_context).ChangeTracker.DetectChanges(); + } + + return _localView ??= new ShardingLocalView(this); + } + } + private ITableRouteManager _tableRouteManager; + + protected ITableRouteManager TableRouteManager + { + get + { + if (null == _tableRouteManager) + { + _tableRouteManager = _shardingRuntimeContext.GetTableRouteManager(); + } + + return _tableRouteManager; + } + } + + private IEntityMetadataManager _entityMetadataManager; + + protected IEntityMetadataManager EntityMetadataManager + { + get + { + if (null == _entityMetadataManager) + { + _entityMetadataManager = _shardingRuntimeContext.GetEntityMetadataManager(); + } + + return _entityMetadataManager; + } + } + + private ITrackerManager _trackerManager; + + protected ITrackerManager TrackerManager + { + get + { + if (null == _trackerManager) + { + _trackerManager = _shardingRuntimeContext.GetTrackerManager(); + } + + return _trackerManager; + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Add(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Add(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async ValueTask> AddAsync( + TEntity entity, + CancellationToken cancellationToken = default) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return await genericDbContext.Set().AddAsync(entity, cancellationToken); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Attach(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Attach(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Remove(TEntity entity) + { + Check.NotNull(entity, nameof(entity)); + + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Remove(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override EntityEntry Update(TEntity entity) + { + var genericDbContext = _context.CreateGenericDbContext(entity); + return genericDbContext.Set().Update(entity); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AddRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AddRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async Task AddRangeAsync(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + await aggregateKv.Key.Set().AddRangeAsync(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AttachRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AttachRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void RemoveRange(params TEntity[] entities) + { + Check.NotNull(entities, nameof(entities)); + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().RemoveRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void UpdateRange(params TEntity[] entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().UpdateRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AddRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AddRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override async Task AddRangeAsync( + IEnumerable entities, + CancellationToken cancellationToken = default) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + await aggregateKv.Key.Set().AddRangeAsync(aggregateKv.Value, cancellationToken); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void AttachRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().AttachRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void RemoveRange(IEnumerable entities) + { + Check.NotNull(entities, nameof(entities)); + + + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().RemoveRange(aggregateKv.Value); + } + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public override void UpdateRange(IEnumerable entities) + { + var aggregateToDic = AggregateToDic(entities); + foreach (var aggregateKv in aggregateToDic) + { + aggregateKv.Key.Set().UpdateRange(aggregateKv.Value); + } + } + + private Dictionary> AggregateToDic(IEnumerable entities) + { + return entities.Select(o => + { + var dbContext = _context.CreateGenericDbContext(o); + return new + { + DbContext = dbContext, + Entity = o + }; + }).GroupBy(g => g.DbContext).ToDictionary(o => o.Key, o => o.Select(g => g.Entity)); + } + + public override TEntity Find(params object[] keyValues) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().Find(keyValues); + } + + return base.Find(keyValues); + } + + public override ValueTask FindAsync(params object[] keyValues) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().FindAsync(keyValues); + } + + return base.FindAsync(keyValues); + } + + public override ValueTask FindAsync(object[] keyValues, CancellationToken cancellationToken) + { + var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); + if (primaryKeyFindDbContext != null) + { + return primaryKeyFindDbContext.Set().FindAsync(keyValues, cancellationToken); + } + + return base.FindAsync(keyValues, cancellationToken); + } + + private DbContext GetDbContextByKeyValue(params object[] keyValues) + { + var entityMetadata = EntityMetadataManager.TryGet(typeof(TEntity)); + if (entityMetadata == null) + { + throw new ShardingCoreInvalidOperationException( + $"cant found type:[{typeof(TEntity)}] in {nameof(IEntityMetadataManager)}"); + } + + //既不是分表也不是分库的话就是默认对象 + if (!entityMetadata.IsShardingTable() && !entityMetadata.IsShardingDataSource()) + { + var defaultDataSourceName = _shardingRuntimeContext.GetVirtualDataSource().DefaultDataSourceName; + var routeTailFactory = _shardingRuntimeContext.GetRouteTailFactory(); + var routeTail = routeTailFactory.Create(string.Empty); + return _context.GetShareDbContext(defaultDataSourceName, routeTail); + } + + if (keyValues.Length == 1) + { + //单key字段 + if (entityMetadata.IsSingleKey) + { + var isShardingDataSource = entityMetadata.IsShardingDataSource(); + var shardingDataSourceFieldIsKey = entityMetadata.ShardingDataSourceFieldIsKey(); + if (isShardingDataSource && !shardingDataSourceFieldIsKey) + return null; + var isShardingTable = entityMetadata.IsShardingTable(); + var shardingTableFieldIsKey = entityMetadata.ShardingTableFieldIsKey(); + if (isShardingTable && !shardingTableFieldIsKey) + return null; + var primaryKeyValue = keyValues[0]; + if (primaryKeyValue != null) + { + var dataSourceName = GetDataSourceName(primaryKeyValue); + var realEntityType = TrackerManager.TranslateEntityType(typeof(TEntity)); + var tableTail = TableRouteManager.GetTableTail(dataSourceName, primaryKeyValue,realEntityType); + var routeTail = _shardingRuntimeContext.GetRouteTailFactory().Create(tableTail); + return _context.GetShareDbContext(dataSourceName, routeTail); + } + } + } + + return null; + } + + private string GetDataSourceName(object shardingKeyValue) + { + return DataSourceRouteManager.GetDataSourceName(shardingKeyValue); + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore6x/ShardingMigrator.cs b/src/ShardingCore/EFCores/EFCore6x/ShardingMigrator.cs new file mode 100644 index 00000000..ef934289 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore6x/ShardingMigrator.cs @@ -0,0 +1,49 @@ + +#if NET6_0&&SHARDINGCORE2_6 +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations.Internal; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Extensions; +using ShardingCore.Helpers; + +namespace ShardingCore.EFCores +{ + public class ShardingMigrator:Migrator + { + private readonly IShardingRuntimeContext _shardingRuntimeContext; + + + public ShardingMigrator(IShardingRuntimeContext shardingRuntimeContext,IMigrationsAssembly migrationsAssembly, IHistoryRepository historyRepository, IDatabaseCreator databaseCreator, IMigrationsSqlGenerator migrationsSqlGenerator, IRawSqlCommandBuilder rawSqlCommandBuilder, IMigrationCommandExecutor migrationCommandExecutor, IRelationalConnection connection, ISqlGenerationHelper sqlGenerationHelper, ICurrentDbContext currentContext, IModelRuntimeInitializer modelRuntimeInitializer, IDiagnosticsLogger logger, IRelationalCommandDiagnosticsLogger commandLogger, IDatabaseProvider databaseProvider) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, currentContext, modelRuntimeInitializer, logger, commandLogger, databaseProvider) + { + _shardingRuntimeContext = shardingRuntimeContext; + + } + public override void Migrate(string targetMigration = null) + { + this.MigrateAsync(targetMigration).WaitAndUnwrapException(false); + // base.Migrate(targetMigration); + } + + public override async Task MigrateAsync(string targetMigration = null, CancellationToken cancellationToken = new CancellationToken()) + { + var virtualDataSource = _shardingRuntimeContext.GetVirtualDataSource(); + var allDataSourceNames = virtualDataSource.GetAllDataSourceNames(); + await DynamicShardingHelper.DynamicMigrateWithDataSourcesAsync(_shardingRuntimeContext, allDataSourceNames, null,targetMigration,cancellationToken).ConfigureAwait(false); + + } + + public override string GenerateScript(string fromMigration = null, string toMigration = null, + MigrationsSqlGenerationOptions options = MigrationsSqlGenerationOptions.Default) + { + return new ScriptMigrationGenerator(_shardingRuntimeContext, fromMigration, toMigration, options).GenerateScript(); + } + } + +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore6x/ShardingModelSource.cs b/src/ShardingCore/EFCores/EFCore6x/ShardingModelSource.cs index 4a24d3a2..3eb4b4e9 100644 --- a/src/ShardingCore/EFCores/EFCore6x/ShardingModelSource.cs +++ b/src/ShardingCore/EFCores/EFCore6x/ShardingModelSource.cs @@ -1,5 +1,5 @@  -#if NET6_0 +#if NET6_0&&SHARDINGCORE2_6 using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; diff --git a/src/ShardingCore/EFCores/EFCore6x/ShardingOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore6x/ShardingOptionsExtension.cs new file mode 100644 index 00000000..41ccdb71 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore6x/ShardingOptionsExtension.cs @@ -0,0 +1,63 @@ +#if NET6_0 && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Core; +using ShardingCore.Core.RuntimeContexts; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + public class ShardingOptionsExtension : IDbContextOptionsExtension + { + public IShardingRuntimeContext ShardingRuntimeContext { get; } + + public ShardingOptionsExtension(IShardingRuntimeContext shardingRuntimeContext) + { + ShardingRuntimeContext = shardingRuntimeContext; + } + + public void ApplyServices(IServiceCollection services) + { + services.AddSingleton(sp => ShardingRuntimeContext); + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new ShardingOptionsExtensionInfo(this); + + private class ShardingOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + private readonly ShardingOptionsExtension _shardingOptionsExtension; + + public ShardingOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) + { + _shardingOptionsExtension = (ShardingOptionsExtension)extension; + } + + public override int GetServiceProviderHashCode() => + _shardingOptionsExtension.ShardingRuntimeContext.GetHashCode(); + + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true; + + public override void PopulateDebugInfo(IDictionary debugInfo) + { + } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "ShardingOptionsExtension"; + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore6x/ShardingWrapOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore6x/ShardingWrapOptionsExtension.cs new file mode 100644 index 00000000..fb9aadb6 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore6x/ShardingWrapOptionsExtension.cs @@ -0,0 +1,56 @@ +#if NET6_0 && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Core; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + + public class ShardingWrapOptionsExtension : IDbContextOptionsExtension + { + + public ShardingWrapOptionsExtension() + { + } + public void ApplyServices(IServiceCollection services) + { + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new ShardingWrapDbContextOptionsExtensionInfo(this); + + private class ShardingWrapDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + public ShardingWrapDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) + { + } + + public override int GetServiceProviderHashCode() => 0; + + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true; + + public override void PopulateDebugInfo(IDictionary debugInfo) + { + } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "ShardingWrapOptionsExtension"; + } + } +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore6x/Tx/ShardingRelationalTransaction.cs b/src/ShardingCore/EFCores/EFCore6x/Tx/ShardingRelationalTransaction.cs new file mode 100644 index 00000000..ad0018a4 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore6x/Tx/ShardingRelationalTransaction.cs @@ -0,0 +1,105 @@ +#if NET6_0&&SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Exceptions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/9/5 20:37:36 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class ShardingRelationalTransaction : RelationalTransaction + { + private readonly IShardingDbContext _shardingDbContext; + private readonly IShardingDbContextExecutor _shardingDbContextExecutor; + + public ShardingRelationalTransaction(IShardingDbContext shardingDbContext, IRelationalConnection connection, DbTransaction transaction, Guid transactionId, IDiagnosticsLogger logger, bool transactionOwned, ISqlGenerationHelper sqlGenerationHelper) : base(connection, transaction, transactionId, logger, transactionOwned, sqlGenerationHelper) + { + _shardingDbContext = + shardingDbContext ?? throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); + _shardingDbContextExecutor = shardingDbContext.GetShardingExecutor() ?? + throw new ShardingCoreInvalidOperationException( + $"{shardingDbContext.GetType()} cant get {nameof(IShardingDbContextExecutor)} from {nameof(shardingDbContext.GetShardingExecutor)}"); + + } + + public override void Commit() + { + base.Commit(); + _shardingDbContextExecutor.Commit(); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + + public override void Rollback() + { + base.Rollback(); + _shardingDbContextExecutor.Rollback(); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + + public override async Task RollbackAsync(CancellationToken cancellationToken = new CancellationToken()) + { + await base.RollbackAsync(cancellationToken); + + await _shardingDbContextExecutor.RollbackAsync(cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + + public override async Task CommitAsync(CancellationToken cancellationToken = new CancellationToken()) + { + await base.CommitAsync(cancellationToken); + + await _shardingDbContextExecutor.CommitAsync(cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + public override void CreateSavepoint(string name) + { + base.CreateSavepoint(name); + _shardingDbContextExecutor.CreateSavepoint(name); + } + + public override async Task CreateSavepointAsync(string name, CancellationToken cancellationToken = new CancellationToken()) + { + await base.CreateSavepointAsync(name, cancellationToken); + await _shardingDbContextExecutor.CreateSavepointAsync(name,cancellationToken); + } + + public override void RollbackToSavepoint(string name) + { + base.RollbackToSavepoint(name); + _shardingDbContextExecutor.RollbackToSavepoint(name); + } + + public override async Task RollbackToSavepointAsync(string name, CancellationToken cancellationToken = new CancellationToken()) + { + await base.RollbackToSavepointAsync(name, cancellationToken); + await _shardingDbContextExecutor.RollbackToSavepointAsync(name,cancellationToken); + } + + public override void ReleaseSavepoint(string name) + { + base.ReleaseSavepoint(name); + _shardingDbContextExecutor.ReleaseSavepoint(name); + } + + public override async Task ReleaseSavepointAsync(string name, CancellationToken cancellationToken = new CancellationToken()) + { + await base.ReleaseSavepointAsync(name, cancellationToken); + await _shardingDbContextExecutor.ReleaseSavepointAsync(name,cancellationToken); + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore6x/Tx/ShardingRelationalTransactionFactory.cs b/src/ShardingCore/EFCores/EFCore6x/Tx/ShardingRelationalTransactionFactory.cs new file mode 100644 index 00000000..83e3ea22 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore6x/Tx/ShardingRelationalTransactionFactory.cs @@ -0,0 +1,39 @@ +#if NET6_0 && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Extensions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/9/5 16:03:04 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + + public class ShardingRelationalTransactionFactory : RelationalTransactionFactory where TShardingDbContext : DbContext, IShardingDbContext + { + private readonly RelationalTransactionFactoryDependencies _dependencies; + public ShardingRelationalTransactionFactory(RelationalTransactionFactoryDependencies dependencies) : base(dependencies) + { + _dependencies = dependencies; + } + public override RelationalTransaction Create(IRelationalConnection connection, DbTransaction transaction, Guid transactionId, + IDiagnosticsLogger logger, bool transactionOwned) + { + var shardingDbContext = connection.Context as IShardingDbContext; + return new ShardingRelationalTransaction(shardingDbContext, connection, transaction, transactionId, logger, transactionOwned, this.Dependencies.SqlGenerationHelper); + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/ShardingRelationalTransactionManager.cs b/src/ShardingCore/EFCores/EFCore6x/Tx/ShardingRelationalTransactionManager.cs similarity index 82% rename from src/ShardingCore/EFCores/ShardingRelationalTransactionManager.cs rename to src/ShardingCore/EFCores/EFCore6x/Tx/ShardingRelationalTransactionManager.cs index 02f5df5c..5f7ee318 100644 --- a/src/ShardingCore/EFCores/ShardingRelationalTransactionManager.cs +++ b/src/ShardingCore/EFCores/EFCore6x/Tx/ShardingRelationalTransactionManager.cs @@ -1,4 +1,6 @@ -using System; +#if NET6_0 && SHARDINGCORE2_6 + +using System; using System.Collections.Generic; using System.Data; using System.Data.Common; @@ -28,31 +30,13 @@ namespace ShardingCore.EFCores private readonly IRelationalConnection _relationalConnection; private readonly IShardingDbContext _shardingDbContext; private readonly IShardingDbContextExecutor _shardingDbContextExecutor; -#if !NETCOREAPP2_0 public ShardingRelationalTransactionManager(IRelationalConnection relationalConnection) { _relationalConnection = relationalConnection; _shardingDbContext = relationalConnection.Context as IShardingDbContext??throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); _shardingDbContextExecutor = _shardingDbContext.GetShardingExecutor(); } -#endif -#if NETCOREAPP2_0 - public ShardingRelationalTransactionManager(IRelationalConnection relationalConnection) - { - _relationalConnection = relationalConnection; - _shardingDbContext = GetDbContext(relationalConnection) as IShardingDbContext??throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); - _shardingDbContextExecutor = _shardingDbContext.GetShardingExecutor(); - } - private DbContext GetDbContext(IRelationalConnection connection) - { - var namedConnectionStringResolver = ((RelationalConnectionDependencies)connection.GetPropertyValue("Dependencies")).ConnectionStringResolver; - var serviceProvider = (IServiceProvider)namedConnectionStringResolver.GetPropertyValue("ApplicationServiceProvider"); - var dbContext = (DbContext)serviceProvider.GetService(typeof(TShardingDbContext)); - return dbContext; - } - -#endif public void ResetState() { _relationalConnection.ResetState(); @@ -102,7 +86,6 @@ namespace ShardingCore.EFCores _shardingDbContextExecutor.NotifyShardingTransaction(); return dbContextTransaction; } -#if !NETCOREAPP2_0 public Task ResetStateAsync(CancellationToken cancellationToken = new CancellationToken()) { @@ -115,7 +98,6 @@ namespace ShardingCore.EFCores _shardingDbContextExecutor.NotifyShardingTransaction(); return dbContextTransaction; } -#if !NETCOREAPP3_0 && !NETSTANDARD2_0 public Task CommitTransactionAsync(CancellationToken cancellationToken = new CancellationToken()) { @@ -138,8 +120,7 @@ namespace ShardingCore.EFCores _shardingDbContextExecutor.NotifyShardingTransaction(); return dbContextTransaction; } -#endif -#endif } } +#endif diff --git a/src/ShardingCore/EFCores/EFCore6x/UnionAllMergeOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore6x/UnionAllMergeOptionsExtension.cs new file mode 100644 index 00000000..bb698fef --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore6x/UnionAllMergeOptionsExtension.cs @@ -0,0 +1,51 @@ +#if NET6_0 && SHARDINGCORE2_6 +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + + public class UnionAllMergeOptionsExtension : IDbContextOptionsExtension + { + public void ApplyServices(IServiceCollection services) + { + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new UnionAllMergeDbContextOptionsExtensionInfo(this); + + private class UnionAllMergeDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + public UnionAllMergeDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) + { + } + + public override int GetServiceProviderHashCode() => 0; + + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true; + + public override void PopulateDebugInfo(IDictionary debugInfo) + { + } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "UnionAllMergeOptionsExtension"; + } + } +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore7x/ScriptMigrationGenerator.cs b/src/ShardingCore/EFCores/EFCore7x/ScriptMigrationGenerator.cs new file mode 100644 index 00000000..de411e43 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/ScriptMigrationGenerator.cs @@ -0,0 +1,28 @@ +#if SHARDINGCORE7 +using Microsoft.EntityFrameworkCore.Migrations; +using ShardingCore.Core.RuntimeContexts; + +namespace ShardingCore.EFCores +{ + public sealed class ScriptMigrationGenerator:AbstractScriptMigrationGenerator + { + private readonly string _fromMigration; + private readonly string _toMigration; + private readonly MigrationsSqlGenerationOptions _options; + + public ScriptMigrationGenerator(IShardingRuntimeContext shardingRuntimeContext, string fromMigration = null, + string toMigration = null, + MigrationsSqlGenerationOptions options = MigrationsSqlGenerationOptions.Default) : base(shardingRuntimeContext) + { + _fromMigration = fromMigration; + _toMigration = toMigration; + _options = options; + } + + protected override string GenerateScriptSql(IMigrator migrator) + { + return migrator.GenerateScript(_fromMigration, _toMigration, _options); + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore7x/ShardingChangeTracker.cs b/src/ShardingCore/EFCores/EFCore7x/ShardingChangeTracker.cs new file mode 100644 index 00000000..b289f083 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/ShardingChangeTracker.cs @@ -0,0 +1,132 @@ +#if SHARDINGCORE7 +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +using Microsoft.EntityFrameworkCore.Metadata; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + public class ShardingChangeTracker : ChangeTracker + { + private readonly DbContext _dbContext; + + public ShardingChangeTracker(DbContext context, IStateManager stateManager, IChangeDetector changeDetector, + IModel model, IEntityEntryGraphIterator graphIterator) : base(context, stateManager, changeDetector, model, + graphIterator) + { + _dbContext = context; + } + + public override bool HasChanges() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().Any(o => + o.Value.GetCurrentContexts().Any(r => r.Value.ChangeTracker.HasChanges())); + } + + return base.HasChanges(); + } + + public override IEnumerable Entries() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().SelectMany(o => + o.Value.GetCurrentContexts().SelectMany(cd => cd.Value.ChangeTracker.Entries())); + } + + return base.Entries(); + } + + public override IEnumerable> Entries() + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + return shardingDbContext.GetShardingExecutor().GetCurrentDbContexts().SelectMany(o => + o.Value.GetCurrentContexts().SelectMany(cd => cd.Value.ChangeTracker.Entries())); + } + + return base.Entries(); + } + + public override void DetectChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.DetectChanges()); + return; + } + base.DetectChanges(); + } + + public override void AcceptAllChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.AcceptAllChanges()); + return; + } + base.AcceptAllChanges(); + } + + private void Do(Action action) + { + var dataSourceDbContexts = ((IShardingDbContext)_dbContext).GetShardingExecutor().GetCurrentDbContexts(); + foreach (var dataSourceDbContext in dataSourceDbContexts) + { + var currentContexts = dataSourceDbContext.Value.GetCurrentContexts(); + foreach (var keyValuePair in currentContexts) + { + action(keyValuePair.Value.ChangeTracker); + } + } + } + + public override void TrackGraph(object rootEntity, Action callback) + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity); + genericDbContext.ChangeTracker.TrackGraph(rootEntity,callback); + // Do(c => c.TrackGraph(rootEntity,callback)); + } + } + + public override void TrackGraph(object rootEntity, TState state, Func, bool> callback) where TState : default + { + if (_dbContext is IShardingDbContext shardingDbContext) + { + var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity); + genericDbContext.ChangeTracker.TrackGraph(rootEntity,state,callback); + // Do(c => c.TrackGraph(rootEntity,callback)); + } + } + + public override void CascadeChanges() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.CascadeChanges()); + return; + } + base.CascadeChanges(); + } + + public override void Clear() + { + if (_dbContext is IShardingDbContext) + { + Do(c => c.Clear()); + return; + } + base.Clear(); + } + } +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore7x/ShardingDbSetSource.cs b/src/ShardingCore/EFCores/EFCore7x/ShardingDbSetSource.cs new file mode 100644 index 00000000..0875fda7 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/ShardingDbSetSource.cs @@ -0,0 +1,57 @@ +#if SHARDINGCORE7 + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; +using System; +using System.Collections.Concurrent; +using System.Reflection; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: Saturday, 14 August 2021 10:17:43 + * @Email: 326308290@qq.com + */ + public class ShardingDbSetSource : IDbSetSource + { + + private static readonly MethodInfo _genericCreateSet + = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); + + private readonly ConcurrentDictionary<(Type Type, string Name), Func> _cache + = new ConcurrentDictionary<(Type Type, string Name), Func>(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual object Create(DbContext context, Type type) + => CreateCore(context, type, null, _genericCreateSet); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual object Create(DbContext context, string name, Type type) + => CreateCore(context, type, name, _genericCreateSet); + + private object CreateCore(DbContext context, Type type, string name, MethodInfo createMethod) + => _cache.GetOrAdd( + (type, name), + t => (Func)createMethod + .MakeGenericMethod(t.Type) + .Invoke(null, null))(context, name); + + private static Func CreateSetFactory() + where TEntity : class + => (c, name) => new ShardingInternalDbSet(c, name); + } + +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/ShardingInternalDbSet.cs b/src/ShardingCore/EFCores/EFCore7x/ShardingInternalDbSet.cs similarity index 92% rename from src/ShardingCore/EFCores/ShardingInternalDbSet.cs rename to src/ShardingCore/EFCores/EFCore7x/ShardingInternalDbSet.cs index e11a25a1..2dd60969 100644 --- a/src/ShardingCore/EFCores/ShardingInternalDbSet.cs +++ b/src/ShardingCore/EFCores/EFCore7x/ShardingInternalDbSet.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +#if SHARDINGCORE7 +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Internal; using ShardingCore.Core; @@ -37,21 +38,11 @@ namespace ShardingCore.EFCores private readonly IShardingRuntimeContext _shardingRuntimeContext; private LocalView? _localView; -#if NET5_0 || NETSTANDARD2_1 || NET6_0 - public ShardingInternalDbSet(DbContext context, string entityTypeName) : base(context, entityTypeName) { _context = (IShardingDbContext)context; _shardingRuntimeContext = context.GetShardingRuntimeContext(); } -#endif -#if NETCOREAPP2_0 || NETCOREAPP3_0 || NETSTANDARD2_0 - public ShardingInternalDbSet(DbContext context) : base(context) - { - _context = (IShardingDbContext)context; - _shardingRuntimeContext = context.GetShardingRuntimeContext(); - } -#endif private IDataSourceRouteManager _dataSourceRouteManager; @@ -144,7 +135,6 @@ namespace ShardingCore.EFCores /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// -#if !NETCOREAPP2_0 public override async ValueTask> AddAsync( TEntity entity, CancellationToken cancellationToken = default) @@ -152,16 +142,6 @@ namespace ShardingCore.EFCores var genericDbContext = _context.CreateGenericDbContext(entity); return await genericDbContext.Set().AddAsync(entity, cancellationToken); } -#endif -#if NETCOREAPP2_0 - public override async Task> AddAsync(TEntity entity, CancellationToken cancellationToken = - new CancellationToken()) - { - var genericDbContext = _context.CreateGenericDbContext(entity); - return await genericDbContext.Set().AddAsync(entity, cancellationToken); - } - -#endif /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -381,7 +361,6 @@ namespace ShardingCore.EFCores return base.Find(keyValues); } -#if !NETCOREAPP2_0 public override ValueTask FindAsync(params object[] keyValues) { var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); @@ -403,28 +382,6 @@ namespace ShardingCore.EFCores return base.FindAsync(keyValues, cancellationToken); } -#endif -#if NETCOREAPP2_0 - public override Task FindAsync(params object[] keyValues) - { - var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); - if (primaryKeyFindDbContext != null) - { - return primaryKeyFindDbContext.Set().FindAsync(keyValues); - } - return base.FindAsync(keyValues); - } - - public override Task FindAsync(object[] keyValues, CancellationToken cancellationToken) - { - var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues); - if (primaryKeyFindDbContext != null) - { - return primaryKeyFindDbContext.Set().FindAsync(keyValues, cancellationToken); - } - return base.FindAsync(keyValues, cancellationToken); - } -#endif private DbContext GetDbContextByKeyValue(params object[] keyValues) { @@ -477,4 +434,6 @@ namespace ShardingCore.EFCores return DataSourceRouteManager.GetDataSourceName(shardingKeyValue); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore7x/ShardingMigrator.cs b/src/ShardingCore/EFCores/EFCore7x/ShardingMigrator.cs new file mode 100644 index 00000000..2ed201df --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/ShardingMigrator.cs @@ -0,0 +1,49 @@ + +#if SHARDINGCORE7 +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations.Internal; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Extensions; +using ShardingCore.Helpers; + +namespace ShardingCore.EFCores +{ + public class ShardingMigrator:Migrator + { + private readonly IShardingRuntimeContext _shardingRuntimeContext; + + + public ShardingMigrator(IShardingRuntimeContext shardingRuntimeContext,IMigrationsAssembly migrationsAssembly, IHistoryRepository historyRepository, IDatabaseCreator databaseCreator, IMigrationsSqlGenerator migrationsSqlGenerator, IRawSqlCommandBuilder rawSqlCommandBuilder, IMigrationCommandExecutor migrationCommandExecutor, IRelationalConnection connection, ISqlGenerationHelper sqlGenerationHelper, ICurrentDbContext currentContext, IModelRuntimeInitializer modelRuntimeInitializer, IDiagnosticsLogger logger, IRelationalCommandDiagnosticsLogger commandLogger, IDatabaseProvider databaseProvider) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, currentContext, modelRuntimeInitializer, logger, commandLogger, databaseProvider) + { + _shardingRuntimeContext = shardingRuntimeContext; + + } + public override void Migrate(string targetMigration = null) + { + this.MigrateAsync(targetMigration).WaitAndUnwrapException(false); + // base.Migrate(targetMigration); + } + + public override async Task MigrateAsync(string targetMigration = null, CancellationToken cancellationToken = new CancellationToken()) + { + var virtualDataSource = _shardingRuntimeContext.GetVirtualDataSource(); + var allDataSourceNames = virtualDataSource.GetAllDataSourceNames(); + await DynamicShardingHelper.DynamicMigrateWithDataSourcesAsync(_shardingRuntimeContext, allDataSourceNames, null,targetMigration,cancellationToken).ConfigureAwait(false); + + } + + public override string GenerateScript(string fromMigration = null, string toMigration = null, + MigrationsSqlGenerationOptions options = MigrationsSqlGenerationOptions.Default) + { + return new ScriptMigrationGenerator(_shardingRuntimeContext, fromMigration, toMigration, options).GenerateScript(); + } + } + +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore7x/ShardingModelSource.cs b/src/ShardingCore/EFCores/EFCore7x/ShardingModelSource.cs new file mode 100644 index 00000000..76833318 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/ShardingModelSource.cs @@ -0,0 +1,134 @@ + +#if SHARDINGCORE7 +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.Extensions.Caching.Memory; +using ShardingCore.Core; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; +using ShardingCore.Exceptions; +using ShardingCore.Sharding.Abstractions; +using System; +using System.Threading; + +namespace ShardingCore.EFCores +{ + + public class ShardingModelSource : ModelSource, IShardingModelSource + { + private readonly object _syncObject = new(); + + public ShardingModelSource(ModelSourceDependencies dependencies) : base(dependencies) + { + Check.NotNull(dependencies, nameof(dependencies)); + Dependencies = dependencies; + } + + /// + /// Dependencies for this service. + /// + protected override ModelSourceDependencies Dependencies { get; } + + + /// + /// Gets the model to be used. + /// + /// The context the model is being produced for. + /// The dependencies object used during the creation of the model. + /// Whether the model should contain design-time configuration. + /// The model to be used. + public override IModel GetModel( + DbContext context, + ModelCreationDependencies modelCreationDependencies, + bool designTime) + { + var priority = CacheItemPriority.High; + var size = 200; + var waitSeconds = 3; + if (context is IShardingTableDbContext shardingTableDbContext) + { + if (shardingTableDbContext.RouteTail is null) + { + throw new ShardingCoreInvalidOperationException("db context model is inited before RouteTail set value"); + } + if (shardingTableDbContext.RouteTail is INoCacheRouteTail) + { + var noCacheModel = this.CreateModel(context, modelCreationDependencies.ConventionSetBuilder, modelCreationDependencies.ModelDependencies); + noCacheModel = modelCreationDependencies.ModelRuntimeInitializer.Initialize(noCacheModel, designTime, modelCreationDependencies.ValidationLogger); + return noCacheModel; + } + else + if (shardingTableDbContext.RouteTail is ISingleQueryRouteTail singleQueryRouteTail && singleQueryRouteTail.IsShardingTableQuery()) + { + priority = CacheItemPriority.Normal; + } + } + var cache = Dependencies.MemoryCache; + var cacheKey = Dependencies.ModelCacheKeyFactory.Create(context, designTime); + if (!cache.TryGetValue(cacheKey, out IModel model)) + { + if (context is IShardingModelCacheOption shardingModelCacheOption) + { + priority = shardingModelCacheOption.GetModelCachePriority(); + size = shardingModelCacheOption.GetModelCacheEntrySize(); + waitSeconds = shardingModelCacheOption.GetModelCacheLockObjectSeconds(); + } + // Make sure OnModelCreating really only gets called once, since it may not be thread safe. + var acquire = Monitor.TryEnter(_syncObject, TimeSpan.FromSeconds(waitSeconds)); + if (!acquire) + { + throw new ShardingCoreInvalidOperationException("cache model timeout"); + } + try + { + if (!cache.TryGetValue(cacheKey, out model)) + { + model = CreateModel( + context, modelCreationDependencies.ConventionSetBuilder, modelCreationDependencies.ModelDependencies); + + model = modelCreationDependencies.ModelRuntimeInitializer.Initialize( + model, designTime, modelCreationDependencies.ValidationLogger); + + model = cache.Set(cacheKey, model, new MemoryCacheEntryOptions { Size = size, Priority = priority }); + } + } + finally + { + Monitor.Exit(_syncObject); + } + } + + return model; + } + + public IModelCacheKeyFactory GetModelCacheKeyFactory() + { + return Dependencies.ModelCacheKeyFactory; + } + + public object GetSyncObject() + { + return _syncObject; + } + + public void Remove(object key) + { + var acquire = Monitor.TryEnter(_syncObject, TimeSpan.FromSeconds(3)); + if (!acquire) + { + throw new ShardingCoreInvalidOperationException("cache model timeout"); + } + try + { + + var cache = Dependencies.MemoryCache; + cache.Remove(key); + } + finally + { + Monitor.Exit(_syncObject); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore7x/ShardingOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore7x/ShardingOptionsExtension.cs new file mode 100644 index 00000000..82e48949 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/ShardingOptionsExtension.cs @@ -0,0 +1,63 @@ +#if SHARDINGCORE7 + +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Core.RuntimeContexts; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + public class ShardingOptionsExtension : IDbContextOptionsExtension + { + public IShardingRuntimeContext ShardingRuntimeContext { get; } + + public ShardingOptionsExtension(IShardingRuntimeContext shardingRuntimeContext) + { + ShardingRuntimeContext = shardingRuntimeContext; + } + + public void ApplyServices(IServiceCollection services) + { + services.AddSingleton(sp => ShardingRuntimeContext); + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new ShardingOptionsExtensionInfo(this); + + private class ShardingOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + private readonly ShardingOptionsExtension _shardingOptionsExtension; + + public ShardingOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) + { + _shardingOptionsExtension = (ShardingOptionsExtension)extension; + } + + public override int GetServiceProviderHashCode() => + _shardingOptionsExtension.ShardingRuntimeContext.GetHashCode(); + + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true; + + public override void PopulateDebugInfo(IDictionary debugInfo) + { + } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "ShardingOptionsExtension"; + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore7x/ShardingWrapOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore7x/ShardingWrapOptionsExtension.cs new file mode 100644 index 00000000..b851e65e --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/ShardingWrapOptionsExtension.cs @@ -0,0 +1,56 @@ +#if SHARDINGCORE7 + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Core; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class ShardingWrapOptionsExtension : IDbContextOptionsExtension + { + + public ShardingWrapOptionsExtension() + { + } + public void ApplyServices(IServiceCollection services) + { + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new ShardingWrapDbContextOptionsExtensionInfo(this); + + private class ShardingWrapDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + public ShardingWrapDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) + { + } + + public override int GetServiceProviderHashCode() => 0; + + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true; + + public override void PopulateDebugInfo(IDictionary debugInfo) + { + } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "ShardingWrapOptionsExtension"; + } + } +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore7x/Tx/ShardingRelationalTransaction.cs b/src/ShardingCore/EFCores/EFCore7x/Tx/ShardingRelationalTransaction.cs new file mode 100644 index 00000000..4cf34f7e --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/Tx/ShardingRelationalTransaction.cs @@ -0,0 +1,103 @@ +#if SHARDINGCORE7 +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Exceptions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/9/5 20:37:36 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class ShardingRelationalTransaction : RelationalTransaction + { + private readonly IShardingDbContext _shardingDbContext; + private readonly IShardingDbContextExecutor _shardingDbContextExecutor; + public ShardingRelationalTransaction(IShardingDbContext shardingDbContext, IRelationalConnection connection, DbTransaction transaction, Guid transactionId, IDiagnosticsLogger logger, bool transactionOwned, ISqlGenerationHelper sqlGenerationHelper) : base(connection, transaction, transactionId, logger, transactionOwned, sqlGenerationHelper) + { + _shardingDbContext = + shardingDbContext ?? throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); + _shardingDbContextExecutor = shardingDbContext.GetShardingExecutor() ?? + throw new ShardingCoreInvalidOperationException( + $"{shardingDbContext.GetType()} cant get {nameof(IShardingDbContextExecutor)} from {nameof(shardingDbContext.GetShardingExecutor)}"); + + } + public override void Commit() + { + base.Commit(); + _shardingDbContextExecutor.Commit(); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + + public override void Rollback() + { + base.Rollback(); + _shardingDbContextExecutor.Rollback(); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + + public override async Task RollbackAsync(CancellationToken cancellationToken = new CancellationToken()) + { + await base.RollbackAsync(cancellationToken); + + await _shardingDbContextExecutor.RollbackAsync(cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + + public override async Task CommitAsync(CancellationToken cancellationToken = new CancellationToken()) + { + await base.CommitAsync(cancellationToken); + + await _shardingDbContextExecutor.CommitAsync(cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + } + public override void CreateSavepoint(string name) + { + base.CreateSavepoint(name); + _shardingDbContextExecutor.CreateSavepoint(name); + } + + public override async Task CreateSavepointAsync(string name, CancellationToken cancellationToken = new CancellationToken()) + { + await base.CreateSavepointAsync(name, cancellationToken); + await _shardingDbContextExecutor.CreateSavepointAsync(name,cancellationToken); + } + + public override void RollbackToSavepoint(string name) + { + base.RollbackToSavepoint(name); + _shardingDbContextExecutor.RollbackToSavepoint(name); + } + + public override async Task RollbackToSavepointAsync(string name, CancellationToken cancellationToken = new CancellationToken()) + { + await base.RollbackToSavepointAsync(name, cancellationToken); + await _shardingDbContextExecutor.RollbackToSavepointAsync(name,cancellationToken); + } + + public override void ReleaseSavepoint(string name) + { + base.ReleaseSavepoint(name); + _shardingDbContextExecutor.ReleaseSavepoint(name); + } + + public override async Task ReleaseSavepointAsync(string name, CancellationToken cancellationToken = new CancellationToken()) + { + await base.ReleaseSavepointAsync(name, cancellationToken); + await _shardingDbContextExecutor.ReleaseSavepointAsync(name,cancellationToken); + } + } +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/EFCore7x/Tx/ShardingRelationalTransactionFactory.cs b/src/ShardingCore/EFCores/EFCore7x/Tx/ShardingRelationalTransactionFactory.cs new file mode 100644 index 00000000..01deb643 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/Tx/ShardingRelationalTransactionFactory.cs @@ -0,0 +1,39 @@ +#if SHARDINGCORE7 +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Extensions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/9/5 16:03:04 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + + + public class ShardingRelationalTransactionFactory : RelationalTransactionFactory where TShardingDbContext : DbContext, IShardingDbContext + { + private readonly RelationalTransactionFactoryDependencies _dependencies; + public ShardingRelationalTransactionFactory(RelationalTransactionFactoryDependencies dependencies) : base(dependencies) + { + _dependencies = dependencies; + } + public override RelationalTransaction Create(IRelationalConnection connection, DbTransaction transaction, Guid transactionId, + IDiagnosticsLogger logger, bool transactionOwned) + { + var shardingDbContext = connection.Context as IShardingDbContext; + return new ShardingRelationalTransaction(shardingDbContext, connection, transaction, transactionId, logger, transactionOwned, this.Dependencies.SqlGenerationHelper); + } + } +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore7x/Tx/ShardingRelationalTransactionManager.cs b/src/ShardingCore/EFCores/EFCore7x/Tx/ShardingRelationalTransactionManager.cs new file mode 100644 index 00000000..33690b20 --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/Tx/ShardingRelationalTransactionManager.cs @@ -0,0 +1,125 @@ +#if SHARDINGCORE7 +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/20 10:08:42 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + /// + /// manage transaction + /// + public class ShardingRelationalTransactionManager : IRelationalTransactionManager where TShardingDbContext : DbContext, IShardingDbContext + { + private readonly IRelationalConnection _relationalConnection; + private readonly IShardingDbContext _shardingDbContext; + private readonly IShardingDbContextExecutor _shardingDbContextExecutor; + public ShardingRelationalTransactionManager(IRelationalConnection relationalConnection) + { + _relationalConnection = relationalConnection; + _shardingDbContext = relationalConnection.Context as IShardingDbContext??throw new ShardingCoreInvalidOperationException($"should implement {nameof(IShardingDbContext)}"); + _shardingDbContextExecutor = _shardingDbContext.GetShardingExecutor(); + } + + public void ResetState() + { + _relationalConnection.ResetState(); + } + + public IDbContextTransaction BeginTransaction() + { + return BeginTransaction(IsolationLevel.Unspecified); + } + + public Task BeginTransactionAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return BeginTransactionAsync(IsolationLevel.Unspecified, cancellationToken); + } + + public void CommitTransaction() + { + _relationalConnection.CommitTransaction(); + } + + + public void RollbackTransaction() + { + _relationalConnection.RollbackTransaction(); + } + + + public IDbContextTransaction CurrentTransaction => _relationalConnection.CurrentTransaction; + public IDbContextTransaction BeginTransaction(IsolationLevel isolationLevel) + { + var dbContextTransaction = _relationalConnection.BeginTransaction(isolationLevel); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public async Task BeginTransactionAsync(IsolationLevel isolationLevel, + CancellationToken cancellationToken = new CancellationToken()) + { + var dbContextTransaction = await _relationalConnection.BeginTransactionAsync(isolationLevel, cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public IDbContextTransaction UseTransaction(DbTransaction transaction) + { + var dbContextTransaction = _relationalConnection.UseTransaction(transaction); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public Task ResetStateAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return _relationalConnection.ResetStateAsync(cancellationToken); + } + + public async Task UseTransactionAsync(DbTransaction transaction, CancellationToken cancellationToken = new CancellationToken()) + { + var dbContextTransaction = await _relationalConnection.UseTransactionAsync(transaction, cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + public Task CommitTransactionAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return _relationalConnection.CommitTransactionAsync(cancellationToken); + } + public Task RollbackTransactionAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return _relationalConnection.RollbackTransactionAsync(cancellationToken); + } + public IDbContextTransaction UseTransaction(DbTransaction transaction, Guid transactionId) + { + var dbContextTransaction = _relationalConnection.UseTransaction(transaction, transactionId); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + public async Task UseTransactionAsync(DbTransaction transaction, Guid transactionId, + CancellationToken cancellationToken = new CancellationToken()) + { + var dbContextTransaction = await _relationalConnection.UseTransactionAsync(transaction, transactionId, cancellationToken); + _shardingDbContextExecutor.NotifyShardingTransaction(); + return dbContextTransaction; + } + + } +} +#endif diff --git a/src/ShardingCore/EFCores/EFCore7x/UnionAllMergeOptionsExtension.cs b/src/ShardingCore/EFCores/EFCore7x/UnionAllMergeOptionsExtension.cs new file mode 100644 index 00000000..b2c8ea1f --- /dev/null +++ b/src/ShardingCore/EFCores/EFCore7x/UnionAllMergeOptionsExtension.cs @@ -0,0 +1,51 @@ +#if SHARDINGCORE7 + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/10/17 20:27:12 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class UnionAllMergeOptionsExtension : IDbContextOptionsExtension + { + public void ApplyServices(IServiceCollection services) + { + } + + public void Validate(IDbContextOptions options) + { + } + + + public DbContextOptionsExtensionInfo Info => new UnionAllMergeDbContextOptionsExtensionInfo(this); + + private class UnionAllMergeDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo + { + public UnionAllMergeDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) + { + } + + public override int GetServiceProviderHashCode() => 0; + + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true; + + public override void PopulateDebugInfo(IDictionary debugInfo) + { + } + + public override bool IsDatabaseProvider => false; + public override string LogFragment => "UnionAllMergeOptionsExtension"; + } + } +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/EFCores/OptionsExtensions/ShardingOptionsExtension.cs b/src/ShardingCore/EFCores/OptionsExtensions/ShardingOptionsExtension.cs deleted file mode 100644 index 80118c88..00000000 --- a/src/ShardingCore/EFCores/OptionsExtensions/ShardingOptionsExtension.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.DependencyInjection; -using ShardingCore.Core; -using ShardingCore.Core.RuntimeContexts; - -namespace ShardingCore.EFCores.OptionsExtensions -{ - /* - * @Author: xjm - * @Description: - * @Date: 2021/10/17 20:27:12 - * @Ver: 1.0 - * @Email: 326308290@qq.com - */ -#if !NETCOREAPP2_0 && !NETSTANDARD2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_1 && !NET5_0 && !NET6_0 - error -#endif - -#if NET6_0 - public class ShardingOptionsExtension : IDbContextOptionsExtension - { - public IShardingRuntimeContext ShardingRuntimeContext { get; } - - public ShardingOptionsExtension(IShardingRuntimeContext shardingRuntimeContext) - { - ShardingRuntimeContext = shardingRuntimeContext; - } - - public void ApplyServices(IServiceCollection services) - { - services.AddSingleton(sp => ShardingRuntimeContext); - } - - public void Validate(IDbContextOptions options) - { - } - - - public DbContextOptionsExtensionInfo Info => new ShardingOptionsExtensionInfo(this); - - private class ShardingOptionsExtensionInfo : DbContextOptionsExtensionInfo - { - private readonly ShardingOptionsExtension _shardingOptionsExtension; - - public ShardingOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) - { - _shardingOptionsExtension = (ShardingOptionsExtension)extension; - } - - public override int GetServiceProviderHashCode() => - _shardingOptionsExtension.ShardingRuntimeContext.GetHashCode(); - - public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true; - - public override void PopulateDebugInfo(IDictionary debugInfo) - { - } - - public override bool IsDatabaseProvider => false; - public override string LogFragment => "ShardingOptionsExtension"; - } - } -#endif -#if NETCOREAPP3_0 || NETSTANDARD2_0 || NET5_0 || NETSTANDARD2_1 - public class ShardingOptionsExtension: IDbContextOptionsExtension - { - public IShardingRuntimeContext ShardingRuntimeContext { get; } - - public ShardingOptionsExtension(IShardingRuntimeContext shardingRuntimeContext) - { - ShardingRuntimeContext = shardingRuntimeContext; - } - public void ApplyServices(IServiceCollection services) - { - services.AddSingleton(sp => ShardingRuntimeContext); - } - - public void Validate(IDbContextOptions options) - { - } - - - public DbContextOptionsExtensionInfo Info => new ShardingOptionsExtensionInfo(this); - - private class ShardingOptionsExtensionInfo : DbContextOptionsExtensionInfo - { - private readonly ShardingOptionsExtension _shardingOptionsExtension; - public ShardingOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) - { - _shardingOptionsExtension = (ShardingOptionsExtension)extension; - } - - - public override long GetServiceProviderHashCode() => _shardingOptionsExtension.ShardingRuntimeContext.GetHashCode(); - - public override void PopulateDebugInfo(IDictionary debugInfo) { } - - public override bool IsDatabaseProvider => false; - public override string LogFragment => "ShardingOptionsExtension"; - } - } - -#endif -#if NETCOREAPP2_0 - public class ShardingOptionsExtension: IDbContextOptionsExtension - { - public IShardingRuntimeContext ShardingRuntimeContext { get; } - - public ShardingOptionsExtension(IShardingRuntimeContext shardingRuntimeContext) - { - ShardingRuntimeContext = shardingRuntimeContext; - } - public bool ApplyServices(IServiceCollection services) - { - services.AddSingleton(sp => ShardingRuntimeContext); - return true; - } - public long GetServiceProviderHashCode() => ShardingRuntimeContext.GetHashCode(); - - public void Validate(IDbContextOptions options) - { - } - - public string LogFragment => "ShardingOptionsExtension"; - } -#endif -} \ No newline at end of file diff --git a/src/ShardingCore/EFCores/OptionsExtensions/ShardingWrapOptionsExtension.cs b/src/ShardingCore/EFCores/OptionsExtensions/ShardingWrapOptionsExtension.cs deleted file mode 100644 index 5cf0a760..00000000 --- a/src/ShardingCore/EFCores/OptionsExtensions/ShardingWrapOptionsExtension.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.DependencyInjection; -using ShardingCore.Core; - -namespace ShardingCore.EFCores.OptionsExtensions -{ - /* - * @Author: xjm - * @Description: - * @Date: 2021/10/17 20:27:12 - * @Ver: 1.0 - * @Email: 326308290@qq.com - */ - -#if !NETCOREAPP2_0 && !NETSTANDARD2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_1 && !NET5_0 && !NET6_0 - error -#endif -#if NET6_0 - public class ShardingWrapOptionsExtension : IDbContextOptionsExtension - { - - public ShardingWrapOptionsExtension() - { - } - public void ApplyServices(IServiceCollection services) - { - } - - public void Validate(IDbContextOptions options) - { - } - - - public DbContextOptionsExtensionInfo Info => new ShardingWrapDbContextOptionsExtensionInfo(this); - - private class ShardingWrapDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo - { - public ShardingWrapDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) - { - } - - public override int GetServiceProviderHashCode() => 0; - - public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true; - - public override void PopulateDebugInfo(IDictionary debugInfo) - { - } - - public override bool IsDatabaseProvider => false; - public override string LogFragment => "ShardingWrapOptionsExtension"; - } - } -#endif -#if NETCOREAPP3_0 || NETSTANDARD2_0 || NET5_0 || NETSTANDARD2_1 - - public class ShardingWrapOptionsExtension: IDbContextOptionsExtension - { - public void ApplyServices(IServiceCollection services) - { - } - - public void Validate(IDbContextOptions options) - { - } - - - public DbContextOptionsExtensionInfo Info => new ShardingWrapDbContextOptionsExtensionInfo(this); - - private class ShardingWrapDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo - { - public ShardingWrapDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) { } - - public override long GetServiceProviderHashCode() => 0; - - public override void PopulateDebugInfo(IDictionary debugInfo) { } - - public override bool IsDatabaseProvider => false; - public override string LogFragment => "ShardingWrapOptionsExtension"; - } - } - -#endif -#if NETCOREAPP2_0 - - public class ShardingWrapOptionsExtension: IDbContextOptionsExtension - { - public bool ApplyServices(IServiceCollection services) - { - return false; - } - - public long GetServiceProviderHashCode() => 0; - - public void Validate(IDbContextOptions options) - { - } - - public string LogFragment => "ShardingWrapOptionsExtension"; - } -#endif -} diff --git a/src/ShardingCore/EFCores/OptionsExtensions/UnionAllMergeOptionsExtension.cs b/src/ShardingCore/EFCores/OptionsExtensions/UnionAllMergeOptionsExtension.cs deleted file mode 100644 index 4db6448c..00000000 --- a/src/ShardingCore/EFCores/OptionsExtensions/UnionAllMergeOptionsExtension.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.DependencyInjection; - -namespace ShardingCore.EFCores.OptionsExtensions -{ - /* - * @Author: xjm - * @Description: - * @Date: 2021/10/17 20:27:12 - * @Ver: 1.0 - * @Email: 326308290@qq.com - */ - -#if !NETCOREAPP2_0 && !NETSTANDARD2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_1 && !NET5_0 && !NET6_0 - error -#endif -#if NET6_0 - public class UnionAllMergeOptionsExtension : IDbContextOptionsExtension - { - public void ApplyServices(IServiceCollection services) - { - } - - public void Validate(IDbContextOptions options) - { - } - - - public DbContextOptionsExtensionInfo Info => new UnionAllMergeDbContextOptionsExtensionInfo(this); - - private class UnionAllMergeDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo - { - public UnionAllMergeDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) - { - } - - public override int GetServiceProviderHashCode() => 0; - - public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true; - - public override void PopulateDebugInfo(IDictionary debugInfo) - { - } - - public override bool IsDatabaseProvider => false; - public override string LogFragment => "UnionAllMergeOptionsExtension"; - } - } -#endif -#if NETCOREAPP3_0 || NETSTANDARD2_0 || NET5_0 || NETSTANDARD2_1 - - public class UnionAllMergeOptionsExtension: IDbContextOptionsExtension - { - public void ApplyServices(IServiceCollection services) - { - } - - public void Validate(IDbContextOptions options) - { - } - - - public DbContextOptionsExtensionInfo Info => new UnionAllMergeDbContextOptionsExtensionInfo(this); - - private class UnionAllMergeDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo - { - public UnionAllMergeDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) { } - - public override long GetServiceProviderHashCode() => 0; - - public override void PopulateDebugInfo(IDictionary debugInfo) { } - - public override bool IsDatabaseProvider => false; - public override string LogFragment => "UnionAllMergeOptionsExtension"; - } - } - -#endif -#if NETCOREAPP2_0 - - public class UnionAllMergeOptionsExtension: IDbContextOptionsExtension - { - public bool ApplyServices(IServiceCollection services) - { - return false; - } - - public long GetServiceProviderHashCode() => 0; - - public void Validate(IDbContextOptions options) - { - } - - public string LogFragment => "UnionAllMergeOptionsExtension"; - } -#endif -} diff --git a/src/ShardingCore/EFCores/ChangeTrackers/ShardingChangeTrackerFactory.cs b/src/ShardingCore/EFCores/ShardingChangeTrackerFactory.cs similarity index 96% rename from src/ShardingCore/EFCores/ChangeTrackers/ShardingChangeTrackerFactory.cs rename to src/ShardingCore/EFCores/ShardingChangeTrackerFactory.cs index e77f3f6a..8b4732f6 100644 --- a/src/ShardingCore/EFCores/ChangeTrackers/ShardingChangeTrackerFactory.cs +++ b/src/ShardingCore/EFCores/ShardingChangeTrackerFactory.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; -namespace ShardingCore.EFCores.ChangeTrackers +namespace ShardingCore.EFCores { public class ShardingChangeTrackerFactory:ChangeTrackerFactory diff --git a/src/ShardingCore/EFCores/ShardingDbSetSource.cs b/src/ShardingCore/EFCores/ShardingDbSetSource.cs deleted file mode 100644 index 789ebdcf..00000000 --- a/src/ShardingCore/EFCores/ShardingDbSetSource.cs +++ /dev/null @@ -1,138 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Internal; -using System; -using System.Collections.Concurrent; -using System.Reflection; - -namespace ShardingCore.EFCores -{ - /* - * @Author: xjm - * @Description: - * @Date: Saturday, 14 August 2021 10:17:43 - * @Email: 326308290@qq.com - */ -#if !NETCOREAPP2_0 && !NETSTANDARD2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_1 && !NET5_0 && !NET6_0 - error -#endif -#if NET5_0 || NETSTANDARD2_1 || NET6_0 - public class ShardingDbSetSource : IDbSetSource - { - - private static readonly MethodInfo _genericCreateSet - = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); - - private readonly ConcurrentDictionary<(Type Type, string Name), Func> _cache - = new ConcurrentDictionary<(Type Type, string Name), Func>(); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual object Create(DbContext context, Type type) - => CreateCore(context, type, null, _genericCreateSet); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual object Create(DbContext context, string name, Type type) - => CreateCore(context, type, name, _genericCreateSet); - - private object CreateCore(DbContext context, Type type, string name, MethodInfo createMethod) - => _cache.GetOrAdd( - (type, name), - t => (Func)createMethod - .MakeGenericMethod(t.Type) - .Invoke(null, null))(context, name); - - private static Func CreateSetFactory() - where TEntity : class - => (c, name) => new ShardingInternalDbSet(c, name); - } -#endif -#if NETCOREAPP3_0 || NETSTANDARD2_0 - public class ShardingDbSetSource:IDbSetSource - { - - private static readonly MethodInfo _genericCreateSet - = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); - - private readonly ConcurrentDictionary> _cache - = new ConcurrentDictionary>(); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual object Create(DbContext context, Type type) - => CreateCore(context, type, _genericCreateSet); - - private object CreateCore(DbContext context, Type type, MethodInfo createMethod) - => _cache.GetOrAdd( - type, - t => (Func)createMethod - .MakeGenericMethod(t) - .Invoke(null, null))(context); - - private static Func CreateSetFactory() - where TEntity : class - => c => new ShardingInternalDbSet(c); -} -#endif - -#if NETCOREAPP2_0 - - /// - /// This API supports the Entity Framework Core infrastructure and is not intended to be used - /// directly from your code. This API may change or be removed in future releases. - /// - public class ShardingDbSetSource : IDbSetSource, IDbQuerySource - { - private static readonly MethodInfo _genericCreateSet - = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); - - private static readonly MethodInfo _genericCreateQuery - = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateQueryFactory)); - - private readonly ConcurrentDictionary> _cache - = new ConcurrentDictionary>(); - - /// - /// This API supports the Entity Framework Core infrastructure and is not intended to be used - /// directly from your code. This API may change or be removed in future releases. - /// - public virtual object Create(DbContext context, Type type) - => CreateCore(context, type, _genericCreateSet); - - /// - /// This API supports the Entity Framework Core infrastructure and is not intended to be used - /// directly from your code. This API may change or be removed in future releases. - /// - public virtual object CreateQuery(DbContext context, Type type) - => CreateCore(context, type, _genericCreateQuery); - - private object CreateCore(DbContext context, Type type, MethodInfo createMethod) - => _cache.GetOrAdd( - type, - t => (Func)createMethod - .MakeGenericMethod(t) - .Invoke(null, null))(context); - - private static Func CreateSetFactory() - where TEntity : class - => c => new ShardingInternalDbSet(c); - - private static Func> CreateQueryFactory() - where TQuery : class - => c => new ShardingInternalDbQuery(c); - } -#endif - -} \ No newline at end of file diff --git a/src/ShardingCore/EFCores/ShardingMigrator.cs b/src/ShardingCore/EFCores/ShardingMigrator.cs deleted file mode 100644 index 727748ea..00000000 --- a/src/ShardingCore/EFCores/ShardingMigrator.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Migrations.Internal; -using Microsoft.EntityFrameworkCore.Storage; -using ShardingCore.Core.DbContextCreator; -using ShardingCore.Core.RuntimeContexts; -using ShardingCore.Core.ServiceProviders; -using ShardingCore.Core.ShardingConfigurations; -using ShardingCore.Core.ShardingMigrations.Abstractions; -using ShardingCore.Core.VirtualDatabase.VirtualDataSources; -using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; -using ShardingCore.Exceptions; -using ShardingCore.Extensions; -using ShardingCore.Helpers; -using ShardingCore.Sharding.Abstractions; -using ShardingCore.Sharding.ShardingDbContextExecutors; - -#if !NETCOREAPP2_0 -using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; - -#endif - -namespace ShardingCore.EFCores -{ - public class ShardingMigrator:Migrator - { - private readonly IShardingRuntimeContext _shardingRuntimeContext; - -#if !NETCOREAPP2_0 && !NETSTANDARD2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_1 && !NET5_0 && !NET6_0 - error -#endif - -#if NET6_0 - public ShardingMigrator(IShardingRuntimeContext shardingRuntimeContext,IMigrationsAssembly migrationsAssembly, IHistoryRepository historyRepository, IDatabaseCreator databaseCreator, IMigrationsSqlGenerator migrationsSqlGenerator, IRawSqlCommandBuilder rawSqlCommandBuilder, IMigrationCommandExecutor migrationCommandExecutor, IRelationalConnection connection, ISqlGenerationHelper sqlGenerationHelper, ICurrentDbContext currentContext, IModelRuntimeInitializer modelRuntimeInitializer, IDiagnosticsLogger logger, IRelationalCommandDiagnosticsLogger commandLogger, IDatabaseProvider databaseProvider) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, currentContext, modelRuntimeInitializer, logger, commandLogger, databaseProvider) - { - _shardingRuntimeContext = shardingRuntimeContext; - - } -#endif -#if NET5_0 || NETSTANDARD2_1 - public ShardingMigrator(IShardingRuntimeContext shardingRuntimeContext, IMigrationsAssembly migrationsAssembly, IHistoryRepository historyRepository, IDatabaseCreator databaseCreator, IMigrationsSqlGenerator migrationsSqlGenerator, IRawSqlCommandBuilder rawSqlCommandBuilder, IMigrationCommandExecutor migrationCommandExecutor, IRelationalConnection connection, ISqlGenerationHelper sqlGenerationHelper, ICurrentDbContext currentContext, IConventionSetBuilder conventionSetBuilder, IDiagnosticsLogger logger, IDiagnosticsLogger commandLogger, IDatabaseProvider databaseProvider) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, currentContext, conventionSetBuilder, logger, commandLogger, databaseProvider) - { - _shardingRuntimeContext = shardingRuntimeContext; - } -#endif - -#if NETCOREAPP3_0 || NETSTANDARD2_0 - public ShardingMigrator(IShardingRuntimeContext shardingRuntimeContext, IMigrationsAssembly migrationsAssembly, IHistoryRepository historyRepository, IDatabaseCreator databaseCreator, IMigrationsSqlGenerator migrationsSqlGenerator, IRawSqlCommandBuilder rawSqlCommandBuilder, IMigrationCommandExecutor migrationCommandExecutor, IRelationalConnection connection, ISqlGenerationHelper sqlGenerationHelper, ICurrentDbContext currentContext, IDiagnosticsLogger logger, IDiagnosticsLogger commandLogger, IDatabaseProvider databaseProvider) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, currentContext, logger, commandLogger, databaseProvider) - { - _shardingRuntimeContext = shardingRuntimeContext; - } - -#endif -#if NETCOREAPP2_0 - - public ShardingMigrator(IShardingRuntimeContext shardingRuntimeContext, IMigrationsAssembly migrationsAssembly, IHistoryRepository historyRepository, IDatabaseCreator databaseCreator, IMigrationsSqlGenerator migrationsSqlGenerator, IRawSqlCommandBuilder rawSqlCommandBuilder, IMigrationCommandExecutor migrationCommandExecutor, IRelationalConnection connection, ISqlGenerationHelper sqlGenerationHelper, IDiagnosticsLogger logger, IDatabaseProvider databaseProvider) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, logger, databaseProvider) - { - _shardingRuntimeContext = shardingRuntimeContext; - } - -#endif - public override void Migrate(string targetMigration = null) - { - this.MigrateAsync(targetMigration).WaitAndUnwrapException(false); - // base.Migrate(targetMigration); - } - - public override async Task MigrateAsync(string targetMigration = null, CancellationToken cancellationToken = new CancellationToken()) - { - var virtualDataSource = _shardingRuntimeContext.GetVirtualDataSource(); - var allDataSourceNames = virtualDataSource.GetAllDataSourceNames(); - await DynamicShardingHelper.DynamicMigrateWithDataSourcesAsync(_shardingRuntimeContext, allDataSourceNames, null,targetMigration,cancellationToken).ConfigureAwait(false); - - } -#if NET6_0 || NET5_0 || NETSTANDARD2_1 - - public override string GenerateScript(string fromMigration = null, string toMigration = null, - MigrationsSqlGenerationOptions options = MigrationsSqlGenerationOptions.Default) - { - return new ScriptMigrationGenerator(_shardingRuntimeContext, fromMigration, toMigration, options).GenerateScript(); - } -#endif -#if NETCOREAPP3_0 || NETSTANDARD2_0 || NETCOREAPP2_0 - public override string GenerateScript(string fromMigration = null, string toMigration = null, bool idempotent = false) - { - return new ScriptMigrationGenerator(_shardingRuntimeContext, fromMigration, toMigration, idempotent).GenerateScript(); - } - -#endif - } - -} diff --git a/src/ShardingCore/EFCores/ShardingRelationalTransactionFactory.cs b/src/ShardingCore/EFCores/ShardingRelationalTransactionFactory.cs deleted file mode 100644 index d625cae2..00000000 --- a/src/ShardingCore/EFCores/ShardingRelationalTransactionFactory.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Text; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; -using ShardingCore.Extensions; -using ShardingCore.Sharding.Abstractions; - -namespace ShardingCore.EFCores -{ - /* - * @Author: xjm - * @Description: - * @Date: 2021/9/5 16:03:04 - * @Ver: 1.0 - * @Email: 326308290@qq.com - */ -#if !NETCOREAPP2_0 && !NETSTANDARD2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_1 && !NET5_0 && !NET6_0 - error -#endif -#if NET6_0 - - public class ShardingRelationalTransactionFactory : RelationalTransactionFactory where TShardingDbContext : DbContext, IShardingDbContext - { - private readonly RelationalTransactionFactoryDependencies _dependencies; - public ShardingRelationalTransactionFactory(RelationalTransactionFactoryDependencies dependencies) : base(dependencies) - { - _dependencies = dependencies; - } - public override RelationalTransaction Create(IRelationalConnection connection, DbTransaction transaction, Guid transactionId, - IDiagnosticsLogger logger, bool transactionOwned) - { - var shardingDbContext = connection.Context as IShardingDbContext; - return new ShardingRelationalTransaction(shardingDbContext, connection, transaction, transactionId, logger, transactionOwned, this.Dependencies.SqlGenerationHelper); - } - } -#endif -#if NETCOREAPP3_0 || NETSTANDARD2_0 || NET5_0 || NETSTANDARD2_1 - - public class ShardingRelationalTransactionFactory : RelationalTransactionFactory where TShardingDbContext : DbContext, IShardingDbContext - { - private readonly RelationalTransactionFactoryDependencies _dependencies; - public ShardingRelationalTransactionFactory(RelationalTransactionFactoryDependencies dependencies) : base(dependencies) - { - _dependencies = dependencies; - } - public override RelationalTransaction Create(IRelationalConnection connection, DbTransaction transaction, Guid transactionId, - IDiagnosticsLogger logger, bool transactionOwned) - { - var shardingDbContext = connection.Context as IShardingDbContext; - return new ShardingRelationalTransaction(shardingDbContext, connection, transaction, transactionId, logger, transactionOwned); - } - } -#endif -#if NETCOREAPP2_0 - - public class ShardingRelationalTransactionFactory : RelationalTransactionFactory where TShardingDbContext : DbContext, IShardingDbContext - { - private readonly RelationalTransactionFactoryDependencies _dependencies; - public ShardingRelationalTransactionFactory(RelationalTransactionFactoryDependencies dependencies) : base(dependencies) - { - _dependencies = dependencies; - } - public override RelationalTransaction Create(IRelationalConnection connection, DbTransaction transaction - , IDiagnosticsLogger logger, - bool transactionOwned) - { - var shardingDbContext = GetDbContext(connection) as IShardingDbContext; - return new ShardingRelationalTransaction(shardingDbContext, connection, transaction, logger, - transactionOwned); - } - - private DbContext GetDbContext(IRelationalConnection connection) - { - var namedConnectionStringResolver = ((RelationalConnectionDependencies)connection.GetPropertyValue("Dependencies")).ConnectionStringResolver; - var serviceProvider = (IServiceProvider)namedConnectionStringResolver.GetPropertyValue("ApplicationServiceProvider"); - var dbContext = (DbContext)serviceProvider.GetService(typeof(TShardingDbContext)); - return dbContext; - } - } -#endif -} diff --git a/src/ShardingCore/Extensions/DbContextExtension.cs b/src/ShardingCore/Extensions/DbContextExtensions/DbContextExtensionShardingCore2_6.cs similarity index 99% rename from src/ShardingCore/Extensions/DbContextExtension.cs rename to src/ShardingCore/Extensions/DbContextExtensions/DbContextExtensionShardingCore2_6.cs index c4da52ed..f0d6eec0 100644 --- a/src/ShardingCore/Extensions/DbContextExtension.cs +++ b/src/ShardingCore/Extensions/DbContextExtensions/DbContextExtensionShardingCore2_6.cs @@ -1,4 +1,6 @@ -using System; +#if SHARDINGCORE2_6 + +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; @@ -17,7 +19,6 @@ using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; using ShardingCore.Core.RuntimeContexts; using ShardingCore.EFCores; -using ShardingCore.EFCores.OptionsExtensions; using ShardingCore.Exceptions; using ShardingCore.Utils; @@ -413,4 +414,5 @@ namespace ShardingCore.Extensions } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/ShardingCore/Extensions/DbContextExtensions/DbContextExtensionShardingCore7.cs b/src/ShardingCore/Extensions/DbContextExtensions/DbContextExtensionShardingCore7.cs new file mode 100644 index 00000000..2306f6e0 --- /dev/null +++ b/src/ShardingCore/Extensions/DbContextExtensions/DbContextExtensionShardingCore7.cs @@ -0,0 +1,203 @@ +#if SHARDINGCORE7 +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.EFCores; +using ShardingCore.Exceptions; + +namespace ShardingCore.Extensions +{ + [ExcludeFromCodeCoverage] + public static class DbContextExtension + { + /// + /// 移除所有的分表关系的模型 + /// + /// + public static void RemoveDbContextRelationModelThatIsShardingTable(this DbContext dbContext) + { + + var contextModel = dbContext.GetService().Model; ; + var shardingRuntimeContext = dbContext.GetShardingRuntimeContext(); + var entityMetadataManager = shardingRuntimeContext.GetEntityMetadataManager(); + + var entityTypes = contextModel.GetEntityTypes(); + foreach (var entityType in entityTypes) + { + if (entityType.GetFieldValue("_data") is List _data) + { + _data.Clear(); + } + } + var contextModelRelationalModel = contextModel.GetRelationalModel() as RelationalModel; + var valueTuples = + contextModelRelationalModel.Tables.Where(o =>o.Value.EntityTypeMappings.Any(m => entityMetadataManager.IsShardingTable(m.EntityType.ClrType))).Select(o => o.Key).ToList(); + for (int i = 0; i < valueTuples.Count; i++) + { + contextModelRelationalModel.Tables.Remove(valueTuples[i]); + } + } + /// + /// 移除所有除了仅分库的 + /// + /// + public static void RemoveDbContextAllRelationModelWithoutShardingDataSourceOnly(this DbContext dbContext) + { + + var contextModel = dbContext.GetService().Model; ; + + var shardingRuntimeContext = dbContext.GetShardingRuntimeContext(); + var entityMetadataManager = shardingRuntimeContext.GetEntityMetadataManager(); + + var entityTypes = contextModel.GetEntityTypes(); + foreach (var entityType in entityTypes) + { + if (entityType.GetFieldValue("_data") is List _data) + { + _data.Clear(); + } + } + var contextModelRelationalModel = contextModel.GetRelationalModel() as RelationalModel; + var valueTuples = + contextModelRelationalModel.Tables.Where(o => o.Value.EntityTypeMappings.Any(m => !entityMetadataManager.IsShardingDataSource(m.EntityType.ClrType) ||entityMetadataManager.TryGet(m.EntityType.ClrType)==null)).Select(o => o.Key).ToList(); + for (int i = 0; i < valueTuples.Count; i++) + { + contextModelRelationalModel.Tables.Remove(valueTuples[i]); + } + + } + /// + /// 移除所有的表 + /// + /// + public static void RemoveDbContextAllRelationModel(this DbContext dbContext) + { + + var contextModel = dbContext.GetService().Model; ; + + var contextModelRelationalModel = contextModel.GetRelationalModel() as RelationalModel; + contextModelRelationalModel.Tables.Clear(); + } + + /// + /// 移除所有的除了我指定的那个类型 + /// + /// + /// + public static void RemoveDbContextRelationModelSaveOnlyThatIsNamedType(this DbContext dbContext, + Type shardingType) + { + var contextModel = dbContext.GetService().Model; + var entityTypes = contextModel.GetEntityTypes(); + foreach (var entityType in entityTypes) + { + if (entityType.GetFieldValue("_data") is List _data) + { + _data.Clear(); + } + } + var contextModelRelationalModel = contextModel.GetRelationalModel() as RelationalModel; + var valueTuples = + contextModelRelationalModel.Tables + .Where(o => o.Value.EntityTypeMappings.All(m => m.EntityType.ClrType != shardingType)) + .Select(o => o.Key).ToList(); + for (int i = 0; i < valueTuples.Count; i++) + { + contextModelRelationalModel.Tables.Remove(valueTuples[i]); + } + + } + + public static void RemoveDbContextRelationModelSaveOnlyThatIsNamedType(this DbContext dbContext) + where T:class + { + RemoveDbContextRelationModelSaveOnlyThatIsNamedType(dbContext, typeof(T)); + } + + /// + /// 移除模型缓存 + /// + /// + public static void RemoveModelCache(this DbContext dbContext) + { + var shardingModelSource = dbContext.GetService() as IShardingModelSource; + var modelCacheKeyFactory = shardingModelSource.GetModelCacheKeyFactory(); + object key1 = modelCacheKeyFactory.Create(dbContext,true); + shardingModelSource.Remove(key1); + object key2 = modelCacheKeyFactory.Create(dbContext,false); + shardingModelSource.Remove(key2); + + } + + /// + /// 获取模型创建的锁 + /// + /// + /// + public static object GetModelCacheSyncObject(this DbContext dbContext) + { + IShardingModelSource shardingModelSource = dbContext.GetService() as IShardingModelSource; + return shardingModelSource.GetSyncObject(); + } + + + public static IEnumerable GetPrimaryKeyValues(TEntity entity,IKey primaryKey) where TEntity : class + { + return primaryKey.Properties.Select(o =>entity.GetPropertyValue(o.Name)); + } + + public static TEntity GetAttachedEntity(this DbContext context, TEntity entity) where TEntity:class + { + if (entity == null) { throw new ArgumentNullException(nameof(entity)); } + var entityPrimaryKey = context.Model.FindRuntimeEntityType(entity.GetType()).FindPrimaryKey(); + if (entityPrimaryKey == null) + { + return entity; + } + var primaryKeyValue = GetPrimaryKeyValues(entity, entityPrimaryKey).ToArray(); + if (primaryKeyValue.IsEmpty()) + return null; + var dbContextDependencies = (IDbContextDependencies)typeof(DbContext).GetTypePropertyValue(context, "DbContextDependencies"); + var stateManager = dbContextDependencies.StateManager; + + //var entityIKey = ShardingKeyUtil.GetEntityIKey(entity); + var internalEntityEntry = stateManager.TryGetEntry(entityPrimaryKey, primaryKeyValue); + + if (internalEntityEntry == null) + return null; + return (TEntity)internalEntityEntry.Entity; + //sp.Restart(); + + //var entityEntries = context.ChangeTracker.Entries(); + //sp.Stop(); + //Console.WriteLine($"ChangeTracker.Entries:{sp.ElapsedMilliseconds}毫秒"); + //sp.Restart(); + //var entry = entityEntries.Where(e => e.State != EntityState.Detached && primaryKeyValue.SequenceEqual(ShardingKeyUtil.GetPrimaryKeyValues(e.Entity))).FirstOrDefault(); + //sp.Stop(); + //Console.WriteLine($"ChangeTracker.FirstOrDefault:{sp.ElapsedMilliseconds}毫秒"); + //return entry?.Entity; + } + + public static IShardingRuntimeContext GetShardingRuntimeContext(this DbContext dbContext) + { + var shardingRuntimeContext = dbContext.GetService(); + + if (shardingRuntimeContext == null) + { + throw new ShardingCoreInvalidOperationException($"cant resolve:[{typeof(IShardingRuntimeContext)}],dbcontext:[{dbContext}]"); + } + + return shardingRuntimeContext; + } + + } +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/Extensions/ShardingDbContextExtension.cs b/src/ShardingCore/Extensions/ShardingDbContextExtension.cs index 3681e00f..0eae4f4f 100644 --- a/src/ShardingCore/Extensions/ShardingDbContextExtension.cs +++ b/src/ShardingCore/Extensions/ShardingDbContextExtension.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Query.Internal; using ShardingCore.Core; using ShardingCore.Core.RuntimeContexts; using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; -using ShardingCore.EFCores.OptionsExtensions; +using ShardingCore.EFCores; using ShardingCore.Sharding; using ShardingCore.Sharding.Abstractions; diff --git a/src/ShardingCore/Sharding/AbstractShardingDbContext.cs b/src/ShardingCore/Sharding/AbstractShardingDbContext.cs index 6eb170b1..61c81386 100644 --- a/src/ShardingCore/Sharding/AbstractShardingDbContext.cs +++ b/src/ShardingCore/Sharding/AbstractShardingDbContext.cs @@ -1,18 +1,13 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; -using ShardingCore.EFCores.OptionsExtensions; -using ShardingCore.Extensions; using ShardingCore.Sharding.Abstractions; using ShardingCore.Sharding.ShardingDbContextExecutors; -using System; using System.Collections.Generic; -using System.Data; using System.Linq; using System.Threading; using System.Threading.Tasks; -using ShardingCore.Core.VirtualDatabase.VirtualDataSources; -using ShardingCore.Exceptions; +using ShardingCore.EFCores; namespace ShardingCore.Sharding { diff --git a/src/ShardingCore/Sharding/Parsers/Visitors/ShardingQueryPrepareVisitor.cs b/src/ShardingCore/Sharding/Parsers/Visitors/ShardingQueryPrepareVisitor2_6.cs similarity index 98% rename from src/ShardingCore/Sharding/Parsers/Visitors/ShardingQueryPrepareVisitor.cs rename to src/ShardingCore/Sharding/Parsers/Visitors/ShardingQueryPrepareVisitor2_6.cs index 2809255c..9c14de31 100644 --- a/src/ShardingCore/Sharding/Parsers/Visitors/ShardingQueryPrepareVisitor.cs +++ b/src/ShardingCore/Sharding/Parsers/Visitors/ShardingQueryPrepareVisitor2_6.cs @@ -1,4 +1,5 @@ -using System; +#if SHARDINGCORE2_6 +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -23,9 +24,6 @@ namespace ShardingCore.Sharding.Parsers.Visitors /// Author: xjm /// Created: 2022/5/1 21:43:12 /// Email: 326308290@qq.com -#if !NETCOREAPP2_0 && !NETSTANDARD2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_1 && !NET5_0 && !NET6_0 - error -#endif internal class ShardingQueryPrepareVisitor : ExpressionVisitor { private readonly IShardingDbContext _shardingDbContext; @@ -232,3 +230,5 @@ namespace ShardingCore.Sharding.Parsers.Visitors } } + +#endif \ No newline at end of file diff --git a/src/ShardingCore/Sharding/Parsers/Visitors/ShardingQueryPrepareVisitor7.cs b/src/ShardingCore/Sharding/Parsers/Visitors/ShardingQueryPrepareVisitor7.cs new file mode 100644 index 00000000..5e3c9e65 --- /dev/null +++ b/src/ShardingCore/Sharding/Parsers/Visitors/ShardingQueryPrepareVisitor7.cs @@ -0,0 +1,219 @@ +#if SHARDINGCORE7 +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using ShardingCore.Core; +using ShardingCore.Core.TrackerManagers; +using ShardingCore.Extensions; +using ShardingCore.Extensions.ShardingQueryableExtensions; +using ShardingCore.Sharding.Abstractions; +using ShardingCore.Sharding.Visitors.ShardingExtractParameters; + +namespace ShardingCore.Sharding.Parsers.Visitors +{ + /// + /// + /// + /// Author: xjm + /// Created: 2022/5/1 21:43:12 + /// Email: 326308290@qq.com + internal class ShardingQueryPrepareVisitor : ExpressionVisitor + { + private readonly IShardingDbContext _shardingDbContext; + private bool isNotSupport; + private ShardingQueryableUseConnectionModeOptions shardingQueryableUseConnectionModeOptions; + private ShardingQueryableAsRouteOptions shardingQueryableAsRouteOptions; + private ShardingQueryableReadWriteSeparationOptions shardingQueryableReadWriteSeparationOptions; + private ShardingQueryableAsSequenceOptions shardingQueryableAsSequenceOptions; + + + + private readonly ITrackerManager _trackerManager; + private bool? isNoTracking; + private bool isIgnoreFilter; + private readonly Dictionary shardingEntities = new(); + + public ShardingQueryPrepareVisitor(IShardingDbContext shardingDbContext) + { + _shardingDbContext = shardingDbContext; + _trackerManager =((DbContext)shardingDbContext).GetShardingRuntimeContext() + .GetTrackerManager(); + } + public ShardingPrepareResult GetShardingPrepareResult() + { + return new ShardingPrepareResult(isNotSupport, + shardingQueryableAsRouteOptions, + shardingQueryableUseConnectionModeOptions, + shardingQueryableReadWriteSeparationOptions, + shardingQueryableAsSequenceOptions, + shardingEntities, isNoTracking, isIgnoreFilter); + } + protected override Expression VisitExtension(Expression node) + { + if (node is QueryRootExpression queryRootExpression) + { + TryAddShardingEntities(queryRootExpression.ElementType, null); + } + return base.VisitExtension(node); + } + private void TryAddShardingEntities(Type entityType, IQueryable queryable) + { + if (!shardingEntities.ContainsKey(entityType)) + { + shardingEntities.Add(entityType, queryable); + } + } + + protected override Expression VisitMember + (MemberExpression memberExpression) + { + + //if (memberExpression.IsMemberQueryable()) //2x,3x 路由 单元测试 分表和不分表 + //{ + // Recurse down to see if we can simplify... + var expression = Visit(memberExpression.Expression); + + // If we've ended up with a constant, and it's a property or a field, + // we can simplify ourselves to a constant + if (expression is ConstantExpression) + { + object container = ((ConstantExpression)expression).Value; + var member = memberExpression.Member; + if (member is FieldInfo fieldInfo) + { + object value = fieldInfo.GetValue(container); + if (value is IQueryable queryable) + { + TryAddShardingEntities(queryable.ElementType, queryable); + } + //return Expression.Constant(value); + } + if (member is PropertyInfo propertyInfo) + { + object value = propertyInfo.GetValue(container, null); + if (value is IQueryable queryable) + { + TryAddShardingEntities(queryable.ElementType, queryable); + } + } + } + //} + return base.VisitMember(memberExpression); + } + protected override Expression VisitMethodCall(MethodCallExpression node) + { + + switch (node.Method.Name) + { + case nameof(EntityFrameworkQueryableExtensions.AsNoTracking): isNoTracking = true; break; + case nameof(EntityFrameworkQueryableExtensions.AsTracking): isNoTracking = false; break; + case nameof(EntityFrameworkQueryableExtensions.IgnoreQueryFilters): isIgnoreFilter = true; break; + // case nameof(EntityFrameworkQueryableExtensions.Include): + // case nameof(EntityFrameworkQueryableExtensions.ThenInclude): DiscoverQueryEntities(node); break; + default: + { + if (node.Method.ReturnType.IsMethodReturnTypeQueryableType()&&node.Method.ReturnType.IsGenericType) + { + DiscoverQueryEntities(node); + } + var customerExpression = DiscoverCustomerQueryEntities(node); + if (customerExpression != null) + { + return Visit(customerExpression); + } + } + ; break; + } + + + return base.VisitMethodCall(node); + } + + private Expression DiscoverCustomerQueryEntities(MethodCallExpression node) + { + + if (node.Method.IsGenericMethod) + { + var genericMethodDefinition = node.Method.GetGenericMethodDefinition(); + + // find notsupport extention calls + if (genericMethodDefinition == EntityFrameworkShardingQueryableExtension.NotSupportMethodInfo) + { + isNotSupport = true; + // cut out extension expression + return node.Arguments[0]; + } + else if (genericMethodDefinition == EntityFrameworkShardingQueryableExtension.UseConnectionModeMethodInfo) + { + shardingQueryableUseConnectionModeOptions = node.Arguments + .OfType() + .Where(o => o.Value is ShardingQueryableUseConnectionModeOptions) + .Select(o => (ShardingQueryableUseConnectionModeOptions)o.Value) + .Last(); + return node.Arguments[0]; + } + else if (genericMethodDefinition == EntityFrameworkShardingQueryableExtension.AsRouteMethodInfo) + { + shardingQueryableAsRouteOptions = node.Arguments + .OfType() + .Where(o => o.Value is ShardingQueryableAsRouteOptions) + .Select(o => (ShardingQueryableAsRouteOptions)o.Value) + .Last(); + return node.Arguments[0]; + } + else if (genericMethodDefinition == EntityFrameworkShardingQueryableExtension.AsSequenceModeMethodInfo) + { + shardingQueryableAsSequenceOptions = node.Arguments + .OfType() + .Where(o => o.Value is ShardingQueryableAsSequenceOptions) + .Select(o => (ShardingQueryableAsSequenceOptions)o.Value) + .Last(); + return node.Arguments[0]; + } + } + + return null; + } + + private void DiscoverQueryEntities(MethodCallExpression node) + { + var genericArguments = node.Type.GetGenericArguments(); + for (var i = 0; i < genericArguments.Length; i++) + { + var genericArgument = genericArguments[i]; + + if (typeof(IEnumerable).IsAssignableFrom(genericArgument)) + { + var arguments = genericArgument.GetGenericArguments(); + foreach (var argument in arguments) + { + //if is db context model + if (_trackerManager.IsDbContextModel(argument)) + { + TryAddShardingEntities(argument, null); + } + } + } + + if (!genericArgument.IsSimpleType()) + { + //if is db context model + if (_trackerManager.IsDbContextModel(genericArgument)) + { + TryAddShardingEntities(genericArgument, null); + } + } + } + } + + + } +} +#endif diff --git a/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor.cs b/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor2_6.cs similarity index 99% rename from src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor.cs rename to src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor2_6.cs index 1a5232f3..c5cde54b 100644 --- a/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor.cs +++ b/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor2_6.cs @@ -1,3 +1,4 @@ +#if SHARDINGCORE2_6 using System; using System.Linq; using System.Linq.Expressions; @@ -225,4 +226,6 @@ namespace ShardingCore.Core.Internal.Visitors } } #endif -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor7.cs b/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor7.cs new file mode 100644 index 00000000..514818dd --- /dev/null +++ b/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor7.cs @@ -0,0 +1,188 @@ +#if SHARDINGCORE7 +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Query; +using ShardingCore.Core.Internal.Visitors; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; + +namespace ShardingCore.Core.Internal.Visitors +{ + /* + * @Author: xjm + * @Description: + * @Date: Wednesday, 13 January 2021 16:32:27 + * @Email: 326308290@qq.com + */ + + internal class DbContextInnerMemberReferenceReplaceQueryableVisitor : ShardingExpressionVisitor + { + private readonly DbContext _dbContext; + protected bool RootIsVisit = false; + + public DbContextInnerMemberReferenceReplaceQueryableVisitor(DbContext dbContext) + { + _dbContext = dbContext; + } + + // public override Expression Visit(Expression node) + // { + // Console.WriteLine("1"); + // return base.Visit(node); + // } + + protected override Expression VisitMember + (MemberExpression memberExpression) + { + // Recurse down to see if we can simplify... + if (memberExpression.IsMemberQueryable()) //2x,3x 路由 单元测试 分表和不分表 + { + var expressionValue = GetExpressionValue(memberExpression); + if (expressionValue is IQueryable queryable) + { + return ReplaceMemberExpression(queryable); + } + + if (expressionValue is DbContext dbContext) + { + return ReplaceMemberExpression(dbContext); + } + } + + return base.VisitMember(memberExpression); + } + + private MemberExpression ReplaceMemberExpression(IQueryable queryable) + { + var dbContextReplaceQueryableVisitor = new DbContextReplaceQueryableVisitor(_dbContext); + var newExpression = dbContextReplaceQueryableVisitor.Visit(queryable.Expression); + var newQueryable = dbContextReplaceQueryableVisitor.Source.Provider.CreateQuery(newExpression); + var tempVariableGenericType = typeof(TempVariable<>).GetGenericType0(queryable.ElementType); + var tempVariable = Activator.CreateInstance(tempVariableGenericType, newQueryable); + MemberExpression queryableMemberReplaceExpression = + Expression.Property(ConstantExpression.Constant(tempVariable), nameof(TempVariable.Queryable)); + return queryableMemberReplaceExpression; + } + + private MemberExpression ReplaceMemberExpression(DbContext dbContext) + { + var tempVariableGenericType = typeof(TempDbVariable<>).GetGenericType0(dbContext.GetType()); + var tempVariable = Activator.CreateInstance(tempVariableGenericType, _dbContext); + MemberExpression dbContextMemberReplaceExpression = + Expression.Property(ConstantExpression.Constant(tempVariable), + nameof(TempDbVariable.DbContext)); + return dbContextMemberReplaceExpression; + } + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (RootIsVisit&&node.Method.ReturnType.IsMethodReturnTypeQueryableType()&&node.Method.ReturnType.IsGenericType) + { + var notRoot = node.Arguments.IsEmpty(); + + if (notRoot) + { + var entityType = node.Method.ReturnType.GenericTypeArguments[0]; + + var whereCallExpression = ReplaceMethodCallExpression(node, entityType); + return whereCallExpression; + } + } + + return base.VisitMethodCall(node); + } + + private MethodCallExpression ReplaceMethodCallExpression(MethodCallExpression methodCallExpression, + Type entityType) + { + MethodCallExpression whereCallExpression = Expression.Call( + typeof(IShardingQueryableExtension), + nameof(IShardingQueryableExtension.ReplaceDbContextQueryableWithType), + new Type[] { entityType }, + methodCallExpression, Expression.Constant(_dbContext) + ); + return whereCallExpression; + } + + internal sealed class TempVariable + { + public IQueryable Queryable { get; } + + public TempVariable(IQueryable queryable) + { + Queryable = queryable; + } + + public IQueryable GetQueryable() + { + return Queryable; + } + } + + internal sealed class TempDbVariable + { + public T1 DbContext { get; } + + public TempDbVariable(T1 dbContext) + { + DbContext = dbContext; + } + } + } + internal class DbContextReplaceQueryableVisitor : DbContextInnerMemberReferenceReplaceQueryableVisitor + { + private readonly DbContext _dbContext; + public IQueryable Source; + + public DbContextReplaceQueryableVisitor(DbContext dbContext) : base(dbContext) + { + _dbContext = dbContext; + } + + protected override Expression VisitExtension(Expression node) + { + if (node is QueryRootExpression queryRootExpression) + { + var dbContextDependencies = + typeof(DbContext).GetTypePropertyValue(_dbContext, "DbContextDependencies") as + IDbContextDependencies; + var targetIQ = + (IQueryable)((IDbSetCache)_dbContext).GetOrAddSet(dbContextDependencies.SetSource, + queryRootExpression.ElementType); + + var newQueryable = targetIQ.Provider.CreateQuery(targetIQ.Expression); + if (Source == null) + Source = newQueryable; + //如何替换ef5的set + var replaceQueryRoot = new ReplaceSingleQueryRootExpressionVisitor(); + replaceQueryRoot.Visit(newQueryable.Expression); + RootIsVisit = true; + return base.VisitExtension(replaceQueryRoot.QueryRootExpression); + } + + return base.VisitExtension(node); + } + + internal sealed class ReplaceSingleQueryRootExpressionVisitor : ExpressionVisitor + { + public QueryRootExpression QueryRootExpression { get; set; } + + protected override Expression VisitExtension(Expression node) + { + if (node is QueryRootExpression queryRootExpression) + { + if (QueryRootExpression != null) + throw new ShardingCoreException("replace query root more than one query root"); + QueryRootExpression = queryRootExpression; + } + + return base.VisitExtension(node); + } + } + } +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/Sharding/Visitors/Querys/QueryEntitiesVisitor.cs b/src/ShardingCore/Sharding/Visitors/Querys/QueryEntitiesVisitor2_6.cs similarity index 59% rename from src/ShardingCore/Sharding/Visitors/Querys/QueryEntitiesVisitor.cs rename to src/ShardingCore/Sharding/Visitors/Querys/QueryEntitiesVisitor2_6.cs index 58466f73..a528797e 100644 --- a/src/ShardingCore/Sharding/Visitors/Querys/QueryEntitiesVisitor.cs +++ b/src/ShardingCore/Sharding/Visitors/Querys/QueryEntitiesVisitor2_6.cs @@ -1,3 +1,4 @@ +#if SHARDINGCORE2_6 using System; using System.Collections; using System.Collections.Generic; @@ -17,9 +18,6 @@ namespace ShardingCore.Core.Internal.Visitors.Querys * @Date: Saturday, 20 February 2021 11:14:35 * @Email: 326308290@qq.com */ -#if !NETCOREAPP2_0 && !NETSTANDARD2_0 && !NETCOREAPP3_0 && !NETSTANDARD2_1 && !NET5_0 && !NET6_0 - error -#endif #if NETCOREAPP2_0 || NETCOREAPP3_0 || NETSTANDARD2_0 /// /// 获取分表类型 @@ -213,140 +211,6 @@ namespace ShardingCore.Core.Internal.Visitors.Querys } } #endif - // internal class ShardingEntitiesVisitor : ExpressionVisitor - // { - // private readonly IVirtualTableManager _virtualTableManager; - // private readonly ISet _shardingEntities = new HashSet(); - // - // public ShardingEntitiesVisitor(IVirtualTableManager virtualTableManager) - // { - // _virtualTableManager = virtualTableManager; - // } - // - // public ISet GetShardingEntities() - // { - // return _shardingEntities; - // } - // - // private bool IsShardingKey(Expression expression, out Type shardingEntity) - // { - // if (expression is MemberExpression member - // && _virtualTableManager.IsShardingKey(member.Expression.Type, member.Member.Name)) - // { - // shardingEntity = member.Expression.Type; - // return true; - // } - // - // shardingEntity = null; - // return false; - // } - // - // private bool IsMethodShardingKey(MethodCallExpression methodCallExpression, out Type shardingEntity) - // { - // if (methodCallExpression.Arguments.IsNotEmpty()) - // { - // for (int i = 0; i < methodCallExpression.Arguments.Count; i++) - // { - // if (methodCallExpression.Arguments[i] is MemberExpression member - // && _virtualTableManager.IsShardingKey(member.Expression.Type, member.Member.Name)) - // { - // shardingEntity = member.Expression.Type; - // return true; - // } - // } - // } - // - // shardingEntity = null; - // return false; - // } - // - // protected override Expression VisitMethodCall(MethodCallExpression node) - // { - // var methodName = node.Method.Name; - // switch (methodName) - // { - // case nameof(Queryable.Where): - // ResolveWhere(node); - // break; - // } - // - // - // return base.VisitMethodCall(node); - // } - // - // private void ResolveWhere(MethodCallExpression node) - // { - // if (node.Arguments[1] is UnaryExpression unaryExpression) - // { - // if (unaryExpression.Operand is LambdaExpression lambdaExpression) - // { - // Resolve(lambdaExpression); - // } - // } - // } - // - // - // private void Resolve(Expression expression) - // { - // if (expression is LambdaExpression) - // { - // LambdaExpression lambda = expression as LambdaExpression; - // expression = lambda.Body; - // Resolve(expression); - // } - // - // if (expression is BinaryExpression binaryExpression) //解析二元运算符 - // { - // ParseGetWhere(binaryExpression); - // } - // - // if (expression is UnaryExpression) //解析一元运算符 - // { - // UnaryExpression unary = expression as UnaryExpression; - // if (unary.Operand is MethodCallExpression methodCall1Expression) - // { - // ResolveInFunc(methodCall1Expression, unary.NodeType != ExpressionType.Not); - // } - // } - // - // if (expression is MethodCallExpression methodCallExpression) //解析扩展方法 - // { - // ResolveInFunc(methodCallExpression, true); - // } - // } - // - // private void ResolveInFunc(MethodCallExpression methodCallExpression, bool @in) - // { - // if (methodCallExpression.IsEnumerableContains(methodCallExpression.Method.Name) && IsMethodShardingKey(methodCallExpression, out var shardingEntity)) - // { - // _shardingEntities.Add(shardingEntity); - // } - // } - // - // private void ParseGetWhere(BinaryExpression binaryExpression) - // { - // //递归获取 - // if (binaryExpression.Left is BinaryExpression) - // ParseGetWhere(binaryExpression.Left as BinaryExpression); - // if (binaryExpression.Left is MethodCallExpression methodCallExpression) - // { - // Resolve(methodCallExpression); - // } - // - // if (binaryExpression.Left is UnaryExpression unaryExpression) - // Resolve(unaryExpression); - // - // if (binaryExpression.Right is BinaryExpression) - // ParseGetWhere(binaryExpression.Right as BinaryExpression); - // - // if (IsShardingKey(binaryExpression.Left, out var shardingEntity1)) - // { - // _shardingEntities.Add(shardingEntity1); - // } - // else if (IsShardingKey(binaryExpression.Right, out var shardingEntity2)) - // { - // _shardingEntities.Add(shardingEntity2); - // } - // } - // } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/Sharding/Visitors/Querys/QueryEntitiesVisitor7.cs b/src/ShardingCore/Sharding/Visitors/Querys/QueryEntitiesVisitor7.cs new file mode 100644 index 00000000..63175962 --- /dev/null +++ b/src/ShardingCore/Sharding/Visitors/Querys/QueryEntitiesVisitor7.cs @@ -0,0 +1,117 @@ +#if SHARDINGCORE7 +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using ShardingCore.Core.TrackerManagers; +using ShardingCore.Extensions; + +namespace ShardingCore.Core.Internal.Visitors.Querys +{ + /* + * @Author: xjm + * @Description: + * @Date: Saturday, 20 February 2021 11:14:35 + * @Email: 326308290@qq.com + */ + /// + /// 获取分表类型 + /// + internal class QueryEntitiesVisitor : ExpressionVisitor + { + private readonly ITrackerManager _trackerManager; + private readonly ISet _shardingEntities = new HashSet(); + + public QueryEntitiesVisitor(ITrackerManager trackerManager) + { + _trackerManager = trackerManager; + } + + public ISet GetQueryEntities() + { + return _shardingEntities; + } + + protected override Expression VisitExtension(Expression node) + { + if (node is QueryRootExpression queryRootExpression) + { + _shardingEntities.Add(queryRootExpression.ElementType); + } + return base.VisitExtension(node); + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + var methodName = node.Method.Name; + if (methodName == nameof(EntityFrameworkQueryableExtensions.Include) || methodName == nameof(EntityFrameworkQueryableExtensions.ThenInclude)) + { + var genericArguments = node.Type.GetGenericArguments(); + for (var i = 0; i < genericArguments.Length; i++) + { + var genericArgument = genericArguments[i]; + if (typeof(IEnumerable).IsAssignableFrom(genericArgument)) + { + var arguments = genericArgument.GetGenericArguments(); + foreach (var argument in arguments) + { + //if is db context model + if (_trackerManager.IsDbContextModel(argument)) + { + _shardingEntities.Add(argument); + } + } + } + + if (!genericArgument.IsSimpleType()) + { + //if is db context model + if (_trackerManager.IsDbContextModel(genericArgument)) + { + _shardingEntities.Add(genericArgument); + } + } + } + } + return base.VisitMethodCall(node); + } + protected override Expression VisitMember + (MemberExpression memberExpression) + { + // Recurse down to see if we can simplify... + var expression = Visit(memberExpression.Expression); + + // If we've ended up with a constant, and it's a property or a field, + // we can simplify ourselves to a constant + if (expression is ConstantExpression) + { + object container = ((ConstantExpression)expression).Value; + var member = memberExpression.Member; + if (member is FieldInfo fieldInfo) + { + object value = fieldInfo.GetValue(container); + if (value is IQueryable queryable) + { + _shardingEntities.Add(queryable.ElementType); + } + //return Expression.Constant(value); + } + if (member is PropertyInfo propertyInfo) + { + object value = propertyInfo.GetValue(container, null); + if (value is IQueryable queryable) + { + _shardingEntities.Add(queryable.ElementType); + } + } + } + return base.VisitMember(memberExpression); + } + } +} + +#endif \ No newline at end of file diff --git a/src/ShardingCore/ShardingCore.csproj b/src/ShardingCore/ShardingCore.csproj index 098b6bcc..9768c3a9 100644 --- a/src/ShardingCore/ShardingCore.csproj +++ b/src/ShardingCore/ShardingCore.csproj @@ -1,63 +1,22 @@  - netcoreapp2;netcoreapp3;netstandard2.0;netstandard2.1;net5;net6 - $(SHARDINGCORE) + net6; + $(SHARDINGCORE7) true - TRACE;DEBUG; + TRACE;DEBUG;SHARDINGCORE7; latest https://github.com/dotnetcore/sharding-core logo.png - - true - bin\Release\ShardingCore.2x.xml - latest - - - true - bin\Release\ShardingCore.3x.xml - latest - - - - true - bin\Release\ShardingCore.5x.xml - latest - - - - true - bin\Release\ShardingCore.6x.xml - latest - latest true + bin\Release\ShardingCore.xml + latest - - 1701;1702;1591; - - - 1701;1702;1591; - - - 1701;1702;1591; - - - 1701;1702;1591; - - - 1701;1702;1591; - - - 1701;1702;1591; - - - 1701;1702;1591; - - + 1701;1702;1591; @@ -74,21 +33,9 @@ \ - - - - - - - - - - - - - - - + + + diff --git a/src/ShardingCore/ShardingCoreExtension.cs b/src/ShardingCore/ShardingCoreExtension.cs index e6c36e38..9b0ef9b1 100644 --- a/src/ShardingCore/ShardingCoreExtension.cs +++ b/src/ShardingCore/ShardingCoreExtension.cs @@ -17,7 +17,6 @@ using ShardingCore.Core.VirtualRoutes.DataSourceRoutes.RouteRuleEngine; using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; using ShardingCore.Core.VirtualRoutes.TableRoutes.RoutingRuleEngine; using ShardingCore.EFCores; -using ShardingCore.EFCores.OptionsExtensions; using ShardingCore.Jobs; using ShardingCore.Sharding; using ShardingCore.Sharding.Abstractions; @@ -45,7 +44,6 @@ using ShardingCore.Core.VirtualRoutes.Abstractions; using ShardingCore.Core.VirtualRoutes.DataSourceRoutes; using ShardingCore.Core.VirtualRoutes.TableRoutes; using ShardingCore.DynamicDataSources; -using ShardingCore.EFCores.ChangeTrackers; using ShardingCore.Exceptions; using ShardingCore.Extensions; using ShardingCore.Sharding.MergeContexts; diff --git a/src2_6/ShardingCore.2_6/ShardingCore.2_6.csproj b/src2_6/ShardingCore.2_6/ShardingCore.2_6.csproj new file mode 100644 index 00000000..f14d1659 --- /dev/null +++ b/src2_6/ShardingCore.2_6/ShardingCore.2_6.csproj @@ -0,0 +1,101 @@ + + + netcoreapp2;netcoreapp3;netstandard2.0;netstandard2.1;net5;net6 + $(SHARDINGCORE2_6) + true + TRACE;DEBUG;SHARDINGCORE2_6 + latest + https://github.com/dotnetcore/sharding-core + logo.png + ShardingCore + ShardingCore + + + true + bin\Release\ShardingCore.2x.xml + latest + + + true + bin\Release\ShardingCore.3x.xml + latest + + + + true + bin\Release\ShardingCore.5x.xml + latest + + + + true + bin\Release\ShardingCore.6x.xml + latest + + + latest + + + true + + + 1701;1702;1591; + + + 1701;1702;1591; + + + 1701;1702;1591; + + + 1701;1702;1591; + + + 1701;1702;1591; + + + 1701;1702;1591; + + + 1701;1702;1591; + + + 1701;1702;1591; + + + + + + + + + + + + + + + True + \ + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/ShardingCore.Test/ShardingCore.Test.csproj b/test/ShardingCore.Test/ShardingCore.Test.csproj index 612d9af7..44e42c5e 100644 --- a/test/ShardingCore.Test/ShardingCore.Test.csproj +++ b/test/ShardingCore.Test/ShardingCore.Test.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/test/ShardingCore.Test/ShardingTest.cs b/test/ShardingCore.Test/ShardingTest.cs index 5a5b3cd4..458f67c0 100644 --- a/test/ShardingCore.Test/ShardingTest.cs +++ b/test/ShardingCore.Test/ShardingTest.cs @@ -412,7 +412,8 @@ namespace ShardingCore.Test var list1 = await queryable.ToListAsync(); Assert.Equal(24, list1.Count()); Assert.DoesNotContain(list1, o => o.Name != "name_300"); - + // await _virtualDbContext.Set().Where(o=>o.Age==1).ExecuteUpdateAsync( + // s => s.SetProperty(b => b.Name, b => b.Name + " *Featured!*")); var queryable1 = (from u in _virtualDbContext.Set().Where(o => o.Id == "300") join salary in _virtualDbContext.Set().Where(o => o.DateOfMonth == 202005) on u.Id equals salary.UserId diff --git a/test/ShardingCore.Test2x/ShardingCore.Test2x.csproj b/test/ShardingCore.Test2x/ShardingCore.Test2x.csproj index 25f8769f..b39408d5 100644 --- a/test/ShardingCore.Test2x/ShardingCore.Test2x.csproj +++ b/test/ShardingCore.Test2x/ShardingCore.Test2x.csproj @@ -17,8 +17,8 @@ - - + + \ No newline at end of file diff --git a/test/ShardingCore.Test3x/ShardingCore.Test3x.csproj b/test/ShardingCore.Test3x/ShardingCore.Test3x.csproj index 2be6e926..9cf5a245 100644 --- a/test/ShardingCore.Test3x/ShardingCore.Test3x.csproj +++ b/test/ShardingCore.Test3x/ShardingCore.Test3x.csproj @@ -17,7 +17,7 @@ - - + + diff --git a/test/ShardingCore.Test5x/ShardingCore.Test5x.csproj b/test/ShardingCore.Test5x/ShardingCore.Test5x.csproj index 361f88b1..15c85756 100644 --- a/test/ShardingCore.Test5x/ShardingCore.Test5x.csproj +++ b/test/ShardingCore.Test5x/ShardingCore.Test5x.csproj @@ -20,6 +20,6 @@ - + diff --git a/test/ShardingCore.Test6x/Common/SnowflakeId.cs b/test/ShardingCore.Test6x/Common/SnowflakeId.cs new file mode 100644 index 00000000..1bcdf122 --- /dev/null +++ b/test/ShardingCore.Test6x/Common/SnowflakeId.cs @@ -0,0 +1,196 @@ +using System; +using System.Text; + +namespace ShardingCore.Test6x.Common +{/// + /// 雪花ID + /// Twitter_Snowflake + /// SnowFlake的结构如下(每部分用-分开) + /// 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 + /// 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0 + /// 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)得到的值), + /// 41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69 + /// 这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。 + /// 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId + /// 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号 + /// 总共加起来刚好64位,为一个Long型。 + /// SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分), + /// 并且效率较高,经测试,SnowFlake单机每秒都能够产生出极限4,096,000个ID来 + /// + public class SnowflakeId + { + + // 开始时间截 (new DateTime(2020, 1, 1).ToUniversalTime() - Jan1st1970).TotalMilliseconds + private const long twepoch = 1577808000000L; + + // 机器id所占的位数 + private const int workerIdBits = 5; + + // 数据标识id所占的位数 + private const int datacenterIdBits = 5; + + // 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) + private const long maxWorkerId = -1L ^ (-1L << workerIdBits); + + // 支持的最大数据标识id,结果是31 + private const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); + + // 序列在id中占的位数 + private const int sequenceBits = 12; + + // 数据标识id向左移17位(12+5) + private const int datacenterIdShift = sequenceBits + workerIdBits; + + // 机器ID向左移12位 + private const int workerIdShift = sequenceBits; + + + // 时间截向左移22位(5+5+12) + private const int timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; + + // 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) + private const long sequenceMask = -1L ^ (-1L << sequenceBits); + + // 数据中心ID(0~31) + public long datacenterId { get; private set; } + + // 工作机器ID(0~31) + public long workerId { get; private set; } + + // 毫秒内序列(0~4095) + public long sequence { get; private set; } + + // 上次生成ID的时间截 + public long lastTimestamp { get; private set; } + + + /// + /// 雪花ID + /// + /// 数据中心ID + /// 工作机器ID + public SnowflakeId(long datacenterId, long workerId) + { + if (datacenterId > maxDatacenterId || datacenterId < 0) + { + throw new Exception(string.Format("datacenter Id can't be greater than {0} or less than 0", maxDatacenterId)); + } + if (workerId > maxWorkerId || workerId < 0) + { + throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0", maxWorkerId)); + } + this.workerId = workerId; + this.datacenterId = datacenterId; + this.sequence = 0L; + this.lastTimestamp = -1L; + } + + /// + /// 获得下一个ID + /// + /// + public long NextId() + { + lock (this) + { + long timestamp = GetCurrentTimestamp(); + if (timestamp > lastTimestamp) //时间戳改变,毫秒内序列重置 + { + sequence = 0L; + } + else if (timestamp == lastTimestamp) //如果是同一时间生成的,则进行毫秒内序列 + { + sequence = (sequence + 1) & sequenceMask; + if (sequence == 0) //毫秒内序列溢出 + { + timestamp = GetNextTimestamp(lastTimestamp); //阻塞到下一个毫秒,获得新的时间戳 + } + } + else //当前时间小于上一次ID生成的时间戳,证明系统时钟被回拨,此时需要做回拨处理 + { + sequence = (sequence + 1) & sequenceMask; + if (sequence > 0) + { + timestamp = lastTimestamp; //停留在最后一次时间戳上,等待系统时间追上后即完全度过了时钟回拨问题。 + } + else //毫秒内序列溢出 + { + timestamp = lastTimestamp + 1; //直接进位到下一个毫秒 + } + //throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", lastTimestamp - timestamp)); + } + + lastTimestamp = timestamp; //上次生成ID的时间截 + + //移位并通过或运算拼到一起组成64位的ID + var id = ((timestamp - twepoch) << timestampLeftShift) + | (datacenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; + return id; + } + } + + /// + /// 解析雪花ID + /// + /// + public static DateTime AnalyzeIdToDateTime(long Id) + { + + var timestamp = (Id >> timestampLeftShift); + var time = Jan1st1970.AddMilliseconds(timestamp + twepoch); + return time.ToLocalTime(); + } + + /// + /// 解析雪花ID + /// + /// + public static string AnalyzeId(long Id) + { + StringBuilder sb = new StringBuilder(); + + var timestamp = (Id >> timestampLeftShift); + var time = Jan1st1970.AddMilliseconds(timestamp + twepoch); + sb.Append(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss:fff")); + + var datacenterId = (Id ^ (timestamp << timestampLeftShift)) >> datacenterIdShift; + sb.Append("_" + datacenterId); + + var workerId = (Id ^ ((timestamp << timestampLeftShift) | (datacenterId << datacenterIdShift))) >> workerIdShift; + sb.Append("_" + workerId); + + var sequence = Id & sequenceMask; + sb.Append("_" + sequence); + + return sb.ToString(); + } + + /// + /// 阻塞到下一个毫秒,直到获得新的时间戳 + /// + /// 上次生成ID的时间截 + /// 当前时间戳 + private static long GetNextTimestamp(long lastTimestamp) + { + long timestamp = GetCurrentTimestamp(); + while (timestamp <= lastTimestamp) + { + timestamp = GetCurrentTimestamp(); + } + return timestamp; + } + + /// + /// 获取当前时间戳 + /// + /// + private static long GetCurrentTimestamp() + { + return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds; + } + + private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/LogDay.cs b/test/ShardingCore.Test6x/Domain/Entities/LogDay.cs new file mode 100644 index 00000000..11bd808e --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/LogDay.cs @@ -0,0 +1,12 @@ +using System; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class LogDay + { + public Guid Id { get; set; } + public string LogLevel { get; set; } + public string LogBody { get; set; } + public DateTime LogTime { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/LogDayLong.cs b/test/ShardingCore.Test6x/Domain/Entities/LogDayLong.cs new file mode 100644 index 00000000..fde0bd9b --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/LogDayLong.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class LogDayLong + { + public Guid Id { get; set; } + public string LogLevel { get; set; } + public string LogBody { get; set; } + public long LogTime { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/LogMonthLong.cs b/test/ShardingCore.Test6x/Domain/Entities/LogMonthLong.cs new file mode 100644 index 00000000..86ffdf4f --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/LogMonthLong.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class LogMonthLong + { + public string Id { get; set; } + public string Body { get; set; } + public long LogTime { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/LogNoSharding.cs b/test/ShardingCore.Test6x/Domain/Entities/LogNoSharding.cs new file mode 100644 index 00000000..7d51deba --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/LogNoSharding.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class LogNoSharding + { + public string Id { get; set; } + public string Body { get; set; } + public DateTime CreationTime { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/LogWeekDateTime.cs b/test/ShardingCore.Test6x/Domain/Entities/LogWeekDateTime.cs new file mode 100644 index 00000000..d52b9c6d --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/LogWeekDateTime.cs @@ -0,0 +1,12 @@ +using System; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class LogWeekDateTime + { + + public string Id { get; set; } + public string Body { get; set; } + public DateTime LogTime { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/LogWeekTimeLong.cs b/test/ShardingCore.Test6x/Domain/Entities/LogWeekTimeLong.cs new file mode 100644 index 00000000..16693a96 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/LogWeekTimeLong.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class LogWeekTimeLong + { + + public string Id { get; set; } + public string Body { get; set; } + public long LogTime { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/LogYearDateTime.cs b/test/ShardingCore.Test6x/Domain/Entities/LogYearDateTime.cs new file mode 100644 index 00000000..9224bb47 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/LogYearDateTime.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class LogYearDateTime + { + public Guid Id { get; set; } + public string LogBody { get; set; } + public DateTime LogTime { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/LogYearLong.cs b/test/ShardingCore.Test6x/Domain/Entities/LogYearLong.cs new file mode 100644 index 00000000..d41f0bf3 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/LogYearLong.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class LogYearLong + { + public string Id { get; set; } + public string LogBody { get; set; } + public long LogTime { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/MultiShardingOrder.cs b/test/ShardingCore.Test6x/Domain/Entities/MultiShardingOrder.cs new file mode 100644 index 00000000..3c2aa2c5 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/MultiShardingOrder.cs @@ -0,0 +1,11 @@ +using System; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class MultiShardingOrder + { + public long Id { get; set; } + public string Name { get; set; } + public DateTime CreateTime { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/Order.cs b/test/ShardingCore.Test6x/Domain/Entities/Order.cs new file mode 100644 index 00000000..49e1350c --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/Order.cs @@ -0,0 +1,13 @@ +using System; +using ShardingCore.Core; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class Order + { + public Guid Id { get; set; } + public string Area { get; set; } + public long Money { get; set; } + public DateTime CreateTime { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/SysUserMod.cs b/test/ShardingCore.Test6x/Domain/Entities/SysUserMod.cs new file mode 100644 index 00000000..4bb63c98 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/SysUserMod.cs @@ -0,0 +1,32 @@ +namespace ShardingCore.Test6x.Domain.Entities +{ +/* +* @Author: xjm +* @Description: +* @Date: Thursday, 14 January 2021 15:36:43 +* @Email: 326308290@qq.com +*/ + public class SysUserMod: IId + { + /// + /// 用户Id用于分表 + /// + public string Id { get; set; } + /// + /// 用户名称 + /// + public string Name { get; set; } + /// + /// 用户姓名 + /// + public int Age { get; set; } + public int AgeGroup { get; set; } + + } + + public interface IId + { + string Id { get; set; } + } + +} \ No newline at end of file diff --git a/test/ShardingCore.Test6x/Domain/Entities/SysUserModInt.cs b/test/ShardingCore.Test6x/Domain/Entities/SysUserModInt.cs new file mode 100644 index 00000000..48010fff --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/SysUserModInt.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShardingCore.Test6x.Domain.Entities +{ + public class SysUserModInt + { + /// + /// 用户Id用于分表 + /// + public int Id { get; set; } + /// + /// 用户名称 + /// + public string Name { get; set; } + /// + /// 用户姓名 + /// + public int Age { get; set; } + public int AgeGroup { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Entities/SysUserSalary.cs b/test/ShardingCore.Test6x/Domain/Entities/SysUserSalary.cs new file mode 100644 index 00000000..9c68cf38 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Entities/SysUserSalary.cs @@ -0,0 +1,39 @@ +namespace ShardingCore.Test6x.Domain.Entities +{ +/* +* @Author: xjm +* @Description: +* @Date: Monday, 01 February 2021 15:43:22 +* @Email: 326308290@qq.com +*/ + public class SysUserSalary + { + public string Id { get; set; } + public string UserId { get; set; } + /// + /// 每月的金额 + /// + 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/ShardingCore.Test6x/Domain/Maps/LogDayLongMap.cs b/test/ShardingCore.Test6x/Domain/Maps/LogDayLongMap.cs new file mode 100644 index 00000000..e2003e94 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/LogDayLongMap.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class LogDayLongMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.LogLevel).IsRequired().IsUnicode(false).HasMaxLength(32); + builder.Property(o => o.LogBody).IsRequired().HasMaxLength(256); + builder.ToTable(nameof(LogDayLong)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/LogDayMap.cs b/test/ShardingCore.Test6x/Domain/Maps/LogDayMap.cs new file mode 100644 index 00000000..16900301 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/LogDayMap.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class LogDayMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.LogLevel).IsRequired().IsUnicode(false).HasMaxLength(32); + builder.Property(o => o.LogBody).IsRequired().HasMaxLength(256); + builder.ToTable(nameof(LogDay)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/LogMonthLongMap.cs b/test/ShardingCore.Test6x/Domain/Maps/LogMonthLongMap.cs new file mode 100644 index 00000000..48394270 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/LogMonthLongMap.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class LogMonthLongMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + + builder.HasKey(o => o.Id); + builder.Property(o => o.Id).IsRequired().IsUnicode(false).HasMaxLength(50); + builder.Property(o => o.Body).HasMaxLength(128); + builder.ToTable(nameof(LogMonthLong)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/LogNoShardingMap.cs b/test/ShardingCore.Test6x/Domain/Maps/LogNoShardingMap.cs new file mode 100644 index 00000000..c262930e --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/LogNoShardingMap.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class LogNoShardingMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.Id).IsRequired().HasMaxLength(50); + builder.Property(o => o.Body).HasMaxLength(256); + builder.ToTable(nameof(LogNoSharding)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/LogWeekDateTimeMap.cs b/test/ShardingCore.Test6x/Domain/Maps/LogWeekDateTimeMap.cs new file mode 100644 index 00000000..eb54580d --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/LogWeekDateTimeMap.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class LogWeekDateTimeMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.Id).IsRequired().IsUnicode(false).HasMaxLength(50); + builder.Property(o => o.Body).HasMaxLength(128); + builder.ToTable(nameof(LogWeekDateTime)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/LogWeekTimeLongMap.cs b/test/ShardingCore.Test6x/Domain/Maps/LogWeekTimeLongMap.cs new file mode 100644 index 00000000..081784a0 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/LogWeekTimeLongMap.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class LogWeekTimeLongMap : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.Id).IsRequired().IsUnicode(false).HasMaxLength(50); + builder.Property(o => o.Body).HasMaxLength(128); + builder.ToTable(nameof(LogWeekTimeLong)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/LogYearDateTimeMap.cs b/test/ShardingCore.Test6x/Domain/Maps/LogYearDateTimeMap.cs new file mode 100644 index 00000000..40d69331 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/LogYearDateTimeMap.cs @@ -0,0 +1,22 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class LogYearDateTimeMap : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.LogBody).IsRequired().HasMaxLength(256); + builder.ToTable(nameof(LogYearDateTime)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/LogYearLongMap.cs b/test/ShardingCore.Test6x/Domain/Maps/LogYearLongMap.cs new file mode 100644 index 00000000..996d36bc --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/LogYearLongMap.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class LogYearLongMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + + builder.HasKey(o => o.Id); + builder.Property(o => o.Id).IsRequired().IsUnicode(false).HasMaxLength(50); + builder.Property(o => o.LogBody).HasMaxLength(128); + builder.ToTable(nameof(LogYearLong)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/MultiShardingOrderMap.cs b/test/ShardingCore.Test6x/Domain/Maps/MultiShardingOrderMap.cs new file mode 100644 index 00000000..e59c69c7 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/MultiShardingOrderMap.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class MultiShardingOrderMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.Id).ValueGeneratedNever(); + builder.Property(o => o.Name).IsRequired().IsUnicode(false).HasMaxLength(50); + builder.ToTable(nameof(MultiShardingOrder)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/OrderMap.cs b/test/ShardingCore.Test6x/Domain/Maps/OrderMap.cs new file mode 100644 index 00000000..d62f4b6b --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/OrderMap.cs @@ -0,0 +1,16 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class OrderMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(o => o.Id); + builder.Property(o => o.Area).IsRequired().IsUnicode(false).HasMaxLength(20); + builder.ToTable(nameof(Order)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/SysUserModIntMap.cs b/test/ShardingCore.Test6x/Domain/Maps/SysUserModIntMap.cs new file mode 100644 index 00000000..047f0a39 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/SysUserModIntMap.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Domain.Maps +{ + public class SysUserModIntMap:IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + + builder.HasKey(o => o.Id); + builder.Property(o => o.Id).ValueGeneratedNever(); + builder.Property(o => o.Name).HasMaxLength(128); + builder.ToTable(nameof(SysUserModInt)); + } + } +} diff --git a/test/ShardingCore.Test6x/Domain/Maps/SysUserModMap.cs b/test/ShardingCore.Test6x/Domain/Maps/SysUserModMap.cs new file mode 100644 index 00000000..9c67c030 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/SysUserModMap.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.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/ShardingCore.Test6x/Domain/Maps/SysUserSalaryMap.cs b/test/ShardingCore.Test6x/Domain/Maps/SysUserSalaryMap.cs new file mode 100644 index 00000000..2f4550d6 --- /dev/null +++ b/test/ShardingCore.Test6x/Domain/Maps/SysUserSalaryMap.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.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/ShardingCore.Test6x/ShardingCore.Test6x.csproj b/test/ShardingCore.Test6x/ShardingCore.Test6x.csproj new file mode 100644 index 00000000..79f56a99 --- /dev/null +++ b/test/ShardingCore.Test6x/ShardingCore.Test6x.csproj @@ -0,0 +1,25 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + diff --git a/test/ShardingCore.Test6x/ShardingDefaultDbContext.cs b/test/ShardingCore.Test6x/ShardingDefaultDbContext.cs new file mode 100644 index 00000000..5ebec014 --- /dev/null +++ b/test/ShardingCore.Test6x/ShardingDefaultDbContext.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; +using ShardingCore.Sharding; +using ShardingCore.Sharding.Abstractions; +using ShardingCore.Test6x.Domain.Maps; + +namespace ShardingCore.Test6x +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/8/15 10:21:03 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class ShardingDefaultDbContext:AbstractShardingDbContext, IShardingTableDbContext + { + public ShardingDefaultDbContext(DbContextOptions options) : base(options) + { + } + + //protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + //{ + // base.OnConfiguring(optionsBuilder); + // optionsBuilder.UseLazyLoadingProxies(); + //} + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.ApplyConfiguration(new SysUserModMap()); + modelBuilder.ApplyConfiguration(new SysUserSalaryMap()); + modelBuilder.ApplyConfiguration(new OrderMap()); + modelBuilder.ApplyConfiguration(new LogDayMap()); + modelBuilder.ApplyConfiguration(new LogWeekDateTimeMap()); + modelBuilder.ApplyConfiguration(new LogWeekTimeLongMap()); + modelBuilder.ApplyConfiguration(new LogYearDateTimeMap()); + modelBuilder.ApplyConfiguration(new LogNoShardingMap()); + modelBuilder.ApplyConfiguration(new LogMonthLongMap()); + modelBuilder.ApplyConfiguration(new LogYearLongMap()); + modelBuilder.ApplyConfiguration(new SysUserModIntMap()); + modelBuilder.ApplyConfiguration(new LogDayLongMap()); + modelBuilder.ApplyConfiguration(new MultiShardingOrderMap()); + } + + public IRouteTail RouteTail { get; set; } + } +} diff --git a/test/ShardingCore.Test6x/ShardingTest.cs b/test/ShardingCore.Test6x/ShardingTest.cs new file mode 100644 index 00000000..4be95fed --- /dev/null +++ b/test/ShardingCore.Test6x/ShardingTest.cs @@ -0,0 +1,1791 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.QueryRouteManagers.Abstractions; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources.PhysicDataSources; +using ShardingCore.Core.VirtualRoutes.Abstractions; +using ShardingCore.Core.VirtualRoutes.DataSourceRoutes.RouteRuleEngine; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RoutingRuleEngine; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; +using ShardingCore.Extensions.ShardingPageExtensions; +using ShardingCore.Extensions.ShardingQueryableExtensions; +using ShardingCore.Helpers; +using ShardingCore.Sharding; +using ShardingCore.Sharding.ParallelTables; +using ShardingCore.Sharding.ReadWriteConfigurations; +using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions; +using ShardingCore.Sharding.ShardingComparision.Abstractions; +using ShardingCore.Sharding.ShardingDbContextExecutors; +using ShardingCore.Sharding.StreamMergeEngines; +using ShardingCore.TableCreator; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.Utils; +using Xunit; + +namespace ShardingCore.Test6x +{ + + public class ShardingTest + { + private readonly ShardingDefaultDbContext _virtualDbContext; + private readonly IShardingRouteManager _shardingRouteManager; + private readonly ActualConnectionStringManager _connectionStringManager; + private readonly IConfiguration _configuration; + private readonly IEntityMetadataManager _entityMetadataManager; + private readonly IVirtualDataSource _virtualDataSource; + private readonly ITableRouteManager _tableRouteManager; + private readonly IShardingTableCreator _shardingTableCreator; + private readonly IShardingReadWriteManager _shardingReadWriteManager; + private readonly IRouteTailFactory _routeTailFactory; + private readonly IReadWriteConnectorFactory _readWriteConnectorFactory; + private readonly IShardingConnectionStringResolver _shardingConnectionStringResolver; + private readonly ITableRouteRuleEngineFactory _tableRouteRuleEngineFactory; + private readonly IShardingComparer _shardingComparer; + + public ShardingTest(ShardingDefaultDbContext virtualDbContext, IShardingRuntimeContext shardingRuntimeContext, IConfiguration configuration) + { + _virtualDbContext = virtualDbContext; + _shardingRouteManager = shardingRuntimeContext.GetShardingRouteManager(); + _shardingReadWriteManager = shardingRuntimeContext.GetShardingReadWriteManager(); + _virtualDataSource = shardingRuntimeContext.GetVirtualDataSource(); + _connectionStringManager = new ActualConnectionStringManager(_shardingReadWriteManager, _virtualDataSource); + _configuration = configuration; + _entityMetadataManager = shardingRuntimeContext.GetEntityMetadataManager(); + _tableRouteManager = shardingRuntimeContext.GetTableRouteManager(); + _shardingTableCreator = shardingRuntimeContext.GetShardingTableCreator(); + _routeTailFactory = shardingRuntimeContext.GetRouteTailFactory(); + _shardingComparer = shardingRuntimeContext.GetShardingComparer(); + _readWriteConnectorFactory = shardingRuntimeContext.GetReadWriteConnectorFactory(); + _tableRouteRuleEngineFactory = shardingRuntimeContext.GetService(); + var readWriteConnectors = _virtualDataSource.ConfigurationParams.ReadWriteNodeSeparationConfigs.Select(o => _readWriteConnectorFactory.CreateConnector(_virtualDataSource.ConfigurationParams.ReadStrategy.GetValueOrDefault(), o.Key, o.Value)); + _shardingConnectionStringResolver = new ReadWriteShardingConnectionStringResolver(readWriteConnectors, _virtualDataSource.ConfigurationParams.ReadStrategy.GetValueOrDefault(), _readWriteConnectorFactory); + } + // [Fact] + // public void RouteParseCompileCacheTest() + // { + // var expressionEqualityComparer = new RouteParseExpressionEqualityComparer(); + // var virtualTable = _virtualTableManager.GetVirtualTable(); + // var virtualTableRoute = (AbstractShardingOperatorVirtualTableRoute)virtualTable.GetVirtualRoute(); + // var xxxx = "202102"; + // var queryable1 = _virtualDbContext.Set().Where(o => o.DateOfMonth >= 202102); + // var routeParseExpression1 = ShardingUtil.GetRouteParseExpression(queryable1, virtualTableRoute.EntityMetadata, + // (i, op, propertyName) => virtualTableRoute.GetRouteFilter(i, op, propertyName), true); + // var queryable2 = _virtualDbContext.Set().Where(ox => ox.DateOfMonth >= 202102); + // var routeParseExpression2 = ShardingUtil.GetRouteParseExpression(queryable2, virtualTableRoute.EntityMetadata, + // (i, op, propertyName) => virtualTableRoute.GetRouteFilter(i, op, propertyName), true); + // var xxxx1 = 202102; + // var queryable3 = _virtualDbContext.Set().Where(ox => ox.DateOfMonth >= xxxx1); + // var routeParseExpression3 = ShardingUtil.GetRouteParseExpression(queryable3, virtualTableRoute.EntityMetadata, + // (i, op, propertyName) => virtualTableRoute.GetRouteFilter(i, op, propertyName), true); + // var queryable4 = _virtualDbContext.Set().Where(o => o.DateOfMonth >= 202101); + // var routeParseExpression4 = ShardingUtil.GetRouteParseExpression(queryable4, virtualTableRoute.EntityMetadata, + // (i, op, propertyName) => virtualTableRoute.GetRouteFilter(i, op, propertyName), true); + // var queryable5 = _virtualDbContext.Set().Where(o => o.DateOfMonth > 202101); + // var routeParseExpression5 = ShardingUtil.GetRouteParseExpression(queryable5, virtualTableRoute.EntityMetadata, + // (i, op, propertyName) => virtualTableRoute.GetRouteFilter(i, op, propertyName), true); + // var queryable6 = _virtualDbContext.Set().Where(o => o.DateOfMonth == 202101); + // var routeParseExpression6 = ShardingUtil.GetRouteParseExpression(queryable6, virtualTableRoute.EntityMetadata, + // (i, op, propertyName) => virtualTableRoute.GetRouteFilter(i, op, propertyName), true); + // var queryable7 = _virtualDbContext.Set().Where(o => 202101 <= o.DateOfMonth); + // var routeParseExpression7 = ShardingUtil.GetRouteParseExpression(queryable7, virtualTableRoute.EntityMetadata, + // (i, op, propertyName) => virtualTableRoute.GetRouteFilter(i, op, propertyName), true); + // var queryable8 = _virtualDbContext.Set().Where(o => 202101 == o.DateOfMonth); + // var routeParseExpression8 = ShardingUtil.GetRouteParseExpression(queryable8, virtualTableRoute.EntityMetadata, + // (i, op, propertyName) => virtualTableRoute.GetRouteFilter(i, op, propertyName), true); + // Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression2)); + // Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression3)); + // Assert.NotEqual(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression4)); + // Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression4), expressionEqualityComparer.GetHashCode(routeParseExpression5)); + // Assert.NotEqual(expressionEqualityComparer.GetHashCode(routeParseExpression5), expressionEqualityComparer.GetHashCode(routeParseExpression6)); + // Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression4), expressionEqualityComparer.GetHashCode(routeParseExpression7)); + // Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression6), expressionEqualityComparer.GetHashCode(routeParseExpression8)); + // + // } + + [Fact] + public async Task GenericTest() + { + var a = new DefaultPhysicDataSource("aaa", "aaa", true); + var b = new DefaultPhysicDataSource("aaa", "aaa1", false); + Assert.Equal(a, b); + var x = new EntityMetadata(typeof(LogDay)); + var y = new EntityMetadata(typeof(LogDay)); + Assert.Equal(x, y); + var dateTime = new DateTime(2021, 1, 1); + var logDays = Enumerable.Range(0, 100).Select(o => new LogDay() { Id = Guid.NewGuid(), LogLevel = "info", LogBody = o.ToString(), LogTime = dateTime.AddDays(o) }).ToList(); + + var bulkShardingTableEnumerable = _virtualDbContext.BulkShardingTableEnumerable(logDays); + + Assert.Equal(100, bulkShardingTableEnumerable.Count); + var bulkShardingEnumerable = _virtualDbContext.BulkShardingEnumerable(logDays); + Assert.Equal(1, bulkShardingEnumerable.Count); + foreach (var (key, value) in bulkShardingEnumerable) + { + Assert.Equal(100, value.Count); + } + + _virtualDbContext.Set().Where(o => o.Id == "300").ShardingPrint(); + var contains = await _virtualDbContext.Set().Where(o => o.Id == "300").Select(o => o.Id).ContainsAsync("300"); + Assert.True(contains); + + try + { + var x1 = _virtualDataSource.GetPhysicDataSource("abc"); + } + catch (Exception e) + { + Assert.Equal(typeof(ShardingCoreNotFoundException), e.GetType()); + } + + var queryable = new List().Select(o => new SequenceClass { Id = "123", T = o }).AsQueryable(); + var sourceType = queryable.GetType().GetSequenceType(); + Assert.Equal(typeof(SequenceClass), sourceType); + try + { + _shardingTableCreator.CreateTable("A", "202105"); + } + catch (Exception e) + { + Assert.Equal(typeof(ShardingCoreException), e.GetType()); + } + + var orderMetadata = _entityMetadataManager.TryGet(); + Assert.NotNull(orderMetadata); + var isKey1 = orderMetadata.ShardingDataSourceFieldIsKey(); + Assert.False(isKey1); + var isKey2 = orderMetadata.ShardingTableFieldIsKey(); + Assert.False(isKey2); + var userModMetadata = _entityMetadataManager.TryGet(); + Assert.NotNull(userModMetadata); + var isKey3 = userModMetadata.ShardingDataSourceFieldIsKey(); + Assert.False(isKey3); + var isKey4 = userModMetadata.ShardingTableFieldIsKey(); + Assert.True(isKey4); + + await _virtualDbContext.AddRangeAsync(logDays); + var bulkShardingExpression = _virtualDbContext.BulkShardingExpression(o => new[] { "A", "B" }.Contains(o.Area)); + + Assert.Equal(2, bulkShardingExpression.Count); + Assert.True(bulkShardingExpression.ContainsKey("A")); + Assert.True(bulkShardingExpression.ContainsKey("B")); + + var bulkShardingTableExpression = _virtualDbContext.BulkShardingTableExpression(o => o.Id == Guid.NewGuid().ToString()); + Assert.Equal(1, bulkShardingTableExpression.Count()); + + var noShardingExpression = _virtualDbContext.BulkShardingExpression(o => o.Id == "123"); + Assert.Equal(1, noShardingExpression.Count()); + + var isShardingDbContext = _virtualDbContext.IsShardingDbContext(); + Assert.True(isShardingDbContext); + var isShardingTableDbContext = _virtualDbContext.IsShardingTableDbContext(); + Assert.True(isShardingTableDbContext); + var shardingDbContext = _virtualDbContext.GetType().IsShardingDbContext(); + Assert.True(shardingDbContext); + var shardingTableDbContext = _virtualDbContext.GetType().IsShardingTableDbContext(); + Assert.True(shardingTableDbContext); + var emptyTailIdentity = new SingleQueryRouteTail(string.Empty).GetRouteTailIdentity(); + var aTailIdentity = new SingleQueryRouteTail("a").GetRouteTailIdentity(); + var bTailIdentity = new SingleQueryRouteTail("b").GetRouteTailIdentity(); + var dics = new SortedDictionary(new NoShardingFirstComparer()); + var dicTails = new List() { emptyTailIdentity, aTailIdentity, bTailIdentity }; + for (int i = 0; i < 10; i++) + { + dics.Clear(); + var reOrderList = dicTails.OrderBy(o => Guid.NewGuid()).ToList(); + foreach (var tail in reOrderList) + { + dics.Add(tail, null); + } + Assert.Equal($"{emptyTailIdentity},{aTailIdentity},{bTailIdentity}", string.Join(",", dics.Keys)); + } + + var x1x1 = new ParallelTableGroupNode(new HashSet() + { new ParallelTableComparerType(typeof(SysUserMod)), new ParallelTableComparerType(typeof(SysUserSalary)) }); + var x2x2 = new ParallelTableGroupNode(new HashSet() + { new ParallelTableComparerType(typeof(SysUserSalary)),new ParallelTableComparerType(typeof(SysUserMod)), }); + Assert.Equal(x1x1, x2x2); + Assert.Equal(x1x1.GetHashCode(), x2x2.GetHashCode()); + var succeedAddConnectionString = _shardingConnectionStringResolver.AddConnectionString("X", "Data Source=localhost;Initial Catalog=ShardingCoreDBC;Integrated Security=True;", null); + Assert.True(succeedAddConnectionString); + var connectionString = _shardingConnectionStringResolver.GetConnectionString("X", null); + Assert.Equal("Data Source=localhost;Initial Catalog=ShardingCoreDBC;Integrated Security=True;", connectionString); + } + + public class SequenceClass + { + public string Id { get; set; } + public string T { get; set; } + } + + [Fact] + public async Task TestMultiShardingProperty() + { + + var multiOrder = await _virtualDbContext.Set().Where(o => o.Id == 232398109278351360).FirstOrDefaultAsync(); + Assert.NotNull(multiOrder); + var allMultiOrders = await _virtualDbContext.Set().ToListAsync(); + Assert.Equal(8, allMultiOrders.Count); + var longs = new[] { 232398109278351360, 255197859283087360 }; + var multiOrders = await _virtualDbContext.Set().Where(o => longs.Contains(o.Id)).ToListAsync(); + Assert.Equal(2, multiOrders.Count); + var multinNotOrders = await _virtualDbContext.Set().Where(o => !longs.Contains(o.Id)).ToListAsync(); + Assert.Equal(6, multinNotOrders.Count); + var dateTime = new DateTime(2021, 11, 1); + var multiOrder404 = await _virtualDbContext.Set().Where(o => o.Id == 250345338962063360 && o.CreateTime < dateTime).FirstOrDefaultAsync(); + Assert.Null(multiOrder404); + } + [Fact] + public void TestEntityMetadataManager() + { + var objMetadata0 = _entityMetadataManager.TryGet(typeof(object)); + Assert.Null(objMetadata0); + var objMetadata1 = _entityMetadataManager.TryGet(); + Assert.Null(objMetadata1); + + var objMetadata2 = _entityMetadataManager.TryGet(typeof(SysUserMod)); + Assert.NotNull(objMetadata2); + var objMetadata3 = _entityMetadataManager.TryGet(); + Assert.NotNull(objMetadata3); + var sysUserModIsShardingTable0 = _entityMetadataManager.IsShardingTable(typeof(SysUserMod)); + Assert.True(sysUserModIsShardingTable0); + var sysUserModIsShardingTable1 = _entityMetadataManager.IsShardingTable(); + Assert.True(sysUserModIsShardingTable1); + var sysUserModIsShardingDataSource0 = _entityMetadataManager.IsShardingDataSource(typeof(SysUserMod)); + Assert.False(sysUserModIsShardingDataSource0); + var sysUserModIsShardingDataSource1 = _entityMetadataManager.IsShardingDataSource(); + Assert.False(sysUserModIsShardingDataSource1); + } + [Fact] + public void TestShardingComparer() + { + var x = new Guid("7CDE28F8-D548-B96D-1C61-39FFE37AE492"); + var y = new Guid("3425D899-291D-921B-DDE4-49FFE37AE493"); + //asc y 0); + //asc x().Where(o=>o.Id=="339"); + // var routeResults1 = _tableRouteRuleEngineFactory.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 = _tableRouteRuleEngineFactory.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_Route_Test() + { + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("00"); + + var mod00s = await _virtualDbContext.Set().ToListAsync(); + Assert.Equal(333, mod00s.Count); + } + var mods = await _virtualDbContext.Set().ToListAsync(); + Assert.Equal(1000, mods.Count); + + var modOrders1 = await _virtualDbContext.Set().OrderBy(o => o.Age).ToListAsync(); + int ascAge = 1; + foreach (var sysUserMod in modOrders1) + { + Assert.Equal(ascAge, sysUserMod.Age); + ascAge++; + } + + + var modOrders2 = await _virtualDbContext.Set().OrderByDescending(o => o.Age).ToListAsync(); + int descAge = 1000; + foreach (var sysUserMod in modOrders2) + { + Assert.Equal(descAge, sysUserMod.Age); + descAge--; + } + } + [Fact] + public async Task ToList_All_Test() + { + + var mods = await _virtualDbContext.Set().ToListAsync(); + Assert.Equal(1000, mods.Count); + + var modOrders1 = await _virtualDbContext.Set().OrderBy(o => o.Age).ToListAsync(); + int ascAge = 1; + foreach (var sysUserMod in modOrders1) + { + Assert.Equal(ascAge, sysUserMod.Age); + ascAge++; + } + + var modOrders2 = await _virtualDbContext.Set().OrderByDescending(o => o.Age).ToListAsync(); + int descAge = 1000; + foreach (var sysUserMod in modOrders2) + { + Assert.Equal(descAge, sysUserMod.Age); + descAge--; + } + + + + var pageResult = await _virtualDbContext.Set().Skip(10).Take(10).OrderByDescending(o => o.Age).ToListAsync(); + Assert.Equal(10, pageResult.Count); + int pageDescAge = 990; + foreach (var sysUserMod in pageResult) + { + Assert.Equal(pageDescAge, sysUserMod.Age); + pageDescAge--; + } + var skip10First = await _virtualDbContext.Set().Skip(10).OrderByDescending(o => o.Age).FirstOrDefaultAsync(); + Assert.Equal(skip10First, pageResult[0]); + } + + [Fact] + public async Task ToList_Join_Test() + { + var list111 = await (from u in _virtualDbContext.Set() + join salary in _virtualDbContext.Set() + on u.Id equals salary.UserId + select new + { + u.Id, + u.Age, + Salary = salary.Salary, + DateOfMonth = salary.DateOfMonth, + Name = u.Name + }).CountAsync(); + var list = await (from u in _virtualDbContext.Set() + join salary in _virtualDbContext.Set() + on u.Id equals salary.UserId + select new + { + u.Id, + u.Age, + Salary = salary.Salary, + DateOfMonth = salary.DateOfMonth, + Name = u.Name + }).ToListAsync(); + + var list2 = list.OrderBy(o => o.Age).Select(o => o.Age).Distinct().ToList(); + 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.ToListAsync(); + Assert.Equal(24, list1.Count()); + Assert.DoesNotContain(list1, o => o.Name != "name_300"); + + var queryable1 = (from u in _virtualDbContext.Set().Where(o => o.Id == "300") + join salary in _virtualDbContext.Set().Where(o => o.DateOfMonth == 202005) + on u.Id equals salary.UserId + select new + { + Salary = salary.Salary, + DateOfMonth = salary.DateOfMonth, + Name = u.Name + }); + var list3 = await queryable1.ToListAsync(); + Assert.Equal(1, list3.Count()); + Assert.Contains(list3, o => o.Name == "name_300"); + var firstOrDefaultAsync = await queryable1.OrderBy(o => o.DateOfMonth).FirstOrDefaultAsync(); + Assert.NotNull(firstOrDefaultAsync); + var firstOrDefault = queryable1.OrderBy(o => o.DateOfMonth).FirstOrDefault(); + Assert.NotNull(firstOrDefault); + Assert.Equal(firstOrDefaultAsync, firstOrDefault); + } + + [Fact] + public async Task ToList_OrderBy_Asc_Desc_Test() + { + var modascs = await _virtualDbContext.Set().OrderBy(o => o.Age).ToListAsync(); + 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).ToListAsync(); + 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 => new List { "1", "2", "3", "4" }.Contains(o.Id)).ToListAsync(); + 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 id = 3; + var mods = await _virtualDbContext.Set().Where(o => o.Id == id.ToString()).ToListAsync(); + Assert.Single(mods); + var mods1 = await _virtualDbContext.Set().Where(o => o.Id == "4").ToListAsync(); + Assert.Single(mods1); + Assert.Equal("3", mods[0].Id); + } + + [Fact] + public async Task ToList_Id_Not_Eq_Test() + { + var methodValue = new MethodValue() { AA = "7" }; + var mods123 = await _virtualDbContext.Set().Where(o => o.Id == methodValue.GetAa()).FirstOrDefaultAsync(); + Assert.NotNull(mods123); + Assert.Equal(mods123.Id, "7"); + var mods12 = await _virtualDbContext.Set().Where(o => new List { "3", "4" }.Contains(o.Id)).ToListAsync(); + Assert.Contains(mods12, o => o.Id == "3" || o.Id == "4"); + var mods = await _virtualDbContext.Set().Where(o => o.Id != "3").ToListAsync(); + Assert.Equal(999, mods.Count); + Assert.DoesNotContain(mods, o => o.Id == "3"); + } + public class MethodValue + { + public string AA { get; set; } + + public string GetAa() + { + return AA; + } + } + + [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).ToListAsync(); + 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).ToListAsync(); + 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").ToListAsync(); + Assert.Single(mods); + Assert.Equal("3", mods[0].Id); + } + + [Fact] + public async Task ToList_Id_Eq_Not_In_Db_Test() + { + var mod1s = await _virtualDbContext.Set().Where(o => string.Compare(((IId)o).Id, "1001") == 0).ToListAsync(); + var mod2s = await _virtualDbContext.Set().Where(o => string.Compare("1001", o.Id) == 0).ToListAsync(); + var mod3s = await _virtualDbContext.Set().Where(o => "1001".CompareTo(o.Id) == 0).ToListAsync(); + var mod4s = await _virtualDbContext.Set().Where(o => o.Id.CompareTo("1001") == 0).ToListAsync(); + var mods = await _virtualDbContext.Set().Where(o => o.Id == "1001").ToListAsync(); + 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").ToListAsync(); + Assert.Empty(mods); + } + + [Fact] + public async Task FirstOrDefault_Order_By_Id_Test() + { + var sysUserModAge = await _virtualDbContext.Set().OrderBy(o => o.Age).FirstOrDefaultAsync(); + Assert.True(sysUserModAge != null && sysUserModAge.Id == "1"); + var sysUserModAgeDesc = await _virtualDbContext.Set().OrderByDescending(o => o.Age).FirstOrDefaultAsync(); + Assert.True(sysUserModAgeDesc != null && sysUserModAgeDesc.Id == "1000"); + var sysUserMod = await _virtualDbContext.Set().OrderBy(o => o.Id).FirstOrDefaultAsync(); + Assert.True(sysUserMod != null && sysUserMod.Id == "1"); + + var sysUserModDesc = await _virtualDbContext.Set().OrderByDescending(o => o.Id).FirstOrDefaultAsync(); + Assert.True(sysUserModDesc != null && sysUserModDesc.Id == "999"); + + } + + + [Fact] + public async Task FirstOrDefault2() + { + var sysUserModabxxxxx1 = await _virtualDbContext.Set().Where(o => o.DateOfMonth >= 202102).FirstOrDefaultAsync(); + var sysUserModabxxxxx71 = await _virtualDbContext.Set().Where(ox => ox.DateOfMonth >= 202102).FirstOrDefaultAsync(); + var xxxx1 = 202102; + var sysUserModabxxxxx72 = await _virtualDbContext.Set().Where(ox => ox.DateOfMonth >= xxxx1).FirstOrDefaultAsync(); + var sysUserModabxxxxx2 = await _virtualDbContext.Set().Where(o => o.DateOfMonth >= 202101).FirstOrDefaultAsync(); + var sysUserModabxxxxx3 = await _virtualDbContext.Set().Where(o => o.DateOfMonth > 202102).FirstOrDefaultAsync(); + var sysUserModabxxxxx4 = await _virtualDbContext.Set().Where(o => o.DateOfMonth == 202102).FirstOrDefaultAsync(); + var sysUserModabxxxxx5 = await _virtualDbContext.Set().Where(o => o.DateOfMonth < 202102).FirstOrDefaultAsync(); + var sysUserModabxxxxx6 = await _virtualDbContext.Set().Where(o => o.DateOfMonth <= 202102).FirstOrDefaultAsync(); + var next = "1"; + var sysUserMod1 = await _virtualDbContext.Set().Where(o => o.Id == next).FirstOrDefaultAsync(); + var sysUserModabxxx = await _virtualDbContext.Set().Where(o => o.Name == "name_2").FirstOrDefaultAsync(); + var sysUserModabxxx11 = await _virtualDbContext.Set().Where(o => o.Name == "name_2" || o.Name == "name_3").FirstOrDefaultAsync(); + var x = new Object[] { "1", "2" }; + var sysUserModab = await _virtualDbContext.Set().Where(o => o.Id.Equals("1")).FirstOrDefaultAsync(); + Assert.NotNull(sysUserModab); + Assert.True(sysUserModab.Id == "1"); + var sysUserModaa = await _virtualDbContext.Set().Where(o => "1".Equals(o.Id)).FirstOrDefaultAsync(); + Assert.NotNull(sysUserModaa); + Assert.True(sysUserModaa.Id == "1"); + var sysUserMod = await _virtualDbContext.Set().Where(o => o.Id == "1").FirstOrDefaultAsync(); + Assert.NotNull(sysUserMod); + Assert.True(sysUserMod.Id == "1"); + Assert.Equal(sysUserModaa, sysUserMod); + var sysUserModxx = await _virtualDbContext.Set().Where(o => x.Contains(o.Id)).FirstOrDefaultAsync(); + Assert.NotNull(sysUserModxx); + Assert.True(x.Contains(sysUserModxx.Id)); + Assert.NotNull(sysUserMod); + var userMod = _virtualDbContext.Set().Find("1"); + Assert.Equal(sysUserMod, userMod); + Assert.True(sysUserMod.Id == "1"); + var user198 = await _virtualDbContext.Set().FirstOrDefaultAsync(o => o.Id == "198"); + Assert.True(user198.Id == "198"); + var userId198 = await _virtualDbContext.Set().Where(o => o.Id == "198").Select(o => o.Id).FirstOrDefaultAsync(); + Assert.Equal(userId198, "198"); + } + + [Fact] + public async Task FirstOrDefault3() + { + var sysUserMod = await _virtualDbContext.Set().Where(o => o.Name == "name_2").FirstOrDefaultAsync(); + Assert.NotNull(sysUserMod); + Assert.Equal("2", sysUserMod.Id); + var sysUserMod2 = await _virtualDbContext.Set().Where(o => o.Name == "name_2").Select(o => new + { + Name = o.Name + }).FirstOrDefaultAsync(); + var sysUserMod1 = await _virtualDbContext.Set().Where(o => o.Name == "name_2").Select(o => new TestClass(o.Name, "") { Id = o.Id }).FirstOrDefaultAsync(); + Assert.NotNull(sysUserMod1); + + } + public class TestClass + { + public string Name { get; } + public string Aa { get; } + public string Id { get; set; } + + public TestClass(string name, string aa) + { + Name = name; + Aa = aa; + } + } + + [Fact] + public async Task FirstOrDefault4() + { + var sysUserMod = await _virtualDbContext.Set().Where(o => o.Id != "1").FirstOrDefaultAsync(); + Assert.NotNull(sysUserMod); + Assert.True(sysUserMod.Id != "1"); + } + + [Fact] + public async Task FirstOrDefault5() + { + var sysUserMod = await _virtualDbContext.Set().Where(o => o.Name == "name_1001").FirstOrDefaultAsync(); + Assert.Null(sysUserMod); + } + + + [Fact] + public async Task Count_Test() + { + var a = await _virtualDbContext.Set().Where(o => o.Name == "name_1000").CountAsync(); + Assert.Equal(1, a); + var b = await _virtualDbContext.Set().Where(o => o.Name != "name_1000").CountAsync(); + Assert.Equal(999, b); + } + + [Fact] + public async Task Sum_Test() + { + var a = await _virtualDbContext.Set().SumAsync(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").SumAsync(o => o.Age); + Assert.Equal(expected - 1000, b); + } + + [Fact] + public async Task Max_Test() + { + var a = await _virtualDbContext.Set().MaxAsync(o => o.Age); + Assert.Equal(1000, a); + var b = await _virtualDbContext.Set().Where(o => o.Name != "name_1000").MaxAsync(o => o.Age); + Assert.Equal(999, b); + var c = await _virtualDbContext.Set().Where(o => o.Age < 500).MaxAsync(o => o.Age); + Assert.Equal(499, c); + var e = await _virtualDbContext.Set().Where(o => o.Age <= 500).MaxAsync(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.MaxAsync(o => o.Salary); + Assert.Equal(1390000, maxSalary); + } + + [Fact] + public async Task Min_Test() + { + var a = await _virtualDbContext.Set().MinAsync(o => o.Age); + Assert.Equal(1, a); + var b = await _virtualDbContext.Set().Where(o => o.Name != "name_1").MinAsync(o => o.Age); + Assert.Equal(2, b); + var c = await _virtualDbContext.Set().Where(o => o.Age > 500).MinAsync(o => o.Age); + Assert.Equal(501, c); + var e = await _virtualDbContext.Set().Where(o => o.Age >= 500).MinAsync(o => o.Age); + Assert.Equal(500, e); + } + + [Fact] + public async Task Any_Test() + { + var a = await _virtualDbContext.Set().AnyAsync(o => o.Age == 100); + Assert.True(a); + var b = await _virtualDbContext.Set().Where(o => o.Name != "name_1").AnyAsync(o => o.Age == 1); + Assert.False(b); + var c = await _virtualDbContext.Set().Where(o => o.Age > 500).AnyAsync(o => o.Age <= 500); + Assert.False(c); + var e = await _virtualDbContext.Set().Where(o => o.Age >= 500).AnyAsync(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) + }).ToListAsync(); + 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_Test1() + { + var ids = new[] { "200", "300" }; + var dateOfMonths = new[] { 202111, 202110 }; + var dateTime = DateTime.Now; + + var sql = from u in _virtualDbContext.Set() + group u by u.UserId + into g + select new + { + UI = g.Key, + x = g.Sum(o => o.SalaryDecimal), + Now = dateTime + }; + var listAsync = await sql.ToListAsync(); + 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) + }).ToListAsync(); + 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 OrderCountTest() + { + var asyncCount = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(320, asyncCount); + var syncCount = _virtualDbContext.Set().Count(); + Assert.Equal(320, syncCount); + + var countA = await _virtualDbContext.Set().CountAsync(o => o.Area == "A"); + var countB = await _virtualDbContext.Set().CountAsync(o => o.Area == "B"); + var countC = await _virtualDbContext.Set().CountAsync(o => o.Area == "C"); + Assert.Equal(320, countA + countB + countC); + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var fourCount = await _virtualDbContext.Set().Where(o => o.CreateTime >= fourBegin && o.CreateTime < fiveBegin).CountAsync(); + Assert.Equal(30, fourCount); + } + [Fact] + public async Task OrderFirstTest() + { + var threeMonth = new DateTime(2021, 3, 1); + var order = await _virtualDbContext.Set().FirstOrDefaultAsync(o => o.CreateTime == threeMonth);//��59�� 1��31��2��28�� + Assert.NotNull(order); + Assert.Equal(59, order.Money); + Assert.Equal("C", order.Area); + } + [Fact] + public async Task OrderOrderTest() + { + var orders = await _virtualDbContext.Set().OrderBy(o => o.CreateTime).ToListAsync(); + Assert.Equal(320, orders.Count); + var i = 0; + foreach (var order in orders) + { + Assert.Equal(i, order.Money); + i++; + } + + var threeMonth = new DateTime(2021, 3, 1); + var orderPage = await _virtualDbContext.Set().Where(o => o.CreateTime > threeMonth).OrderByDescending(o => o.CreateTime).ToShardingPageAsync(1, 20); + Assert.Equal(20, orderPage.Data.Count); + Assert.Equal(260, orderPage.Total); + + + var j = 319; + foreach (var order in orderPage.Data) + { + Assert.Equal(j, order.Money); + j--; + } + + + var orderPage1 = await _virtualDbContext.Set().Where(o => o.CreateTime > threeMonth).OrderBy(o => o.CreateTime).ToShardingPageAsync(1, 20); + Assert.Equal(20, orderPage1.Data.Count); + Assert.Equal(260, orderPage1.Total); + + + var j1 = 60; + foreach (var order in orderPage1.Data) + { + Assert.Equal(j1, order.Money); + j1++; + } + } + + [Fact] + public async Task LogDayCountTest() + { + var countAsync = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(3000, countAsync); + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var fourCount = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).CountAsync(); + Assert.Equal(300, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("20210102"); + var countAsync1 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(10, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("20210103", "20210104"); + var countAsync2 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(20, countAsync2); + } + } + [Fact] + public void LogDayTableSeparatorTest() + { + var tableRoute = _tableRouteManager.GetRoute(typeof(LogDay)); + var virtualTableName = tableRoute.EntityMetadata.LogicTableName; + Assert.Equal(nameof(LogDay), virtualTableName); + + Assert.True(string.IsNullOrWhiteSpace(tableRoute.EntityMetadata.TableSeparator)); + var entityMetadata = _entityMetadataManager.TryGet(); + Assert.NotNull(entityMetadata); + var isShardingTable = entityMetadata.IsShardingTable(); + Assert.True(isShardingTable); + var isShardingDataSource = entityMetadata.IsShardingDataSource(); + Assert.False(isShardingDataSource); + var emptySeparator = string.IsNullOrWhiteSpace(entityMetadata.TableSeparator); + Assert.True(emptySeparator); + Assert.Null(entityMetadata.AutoCreateDataSourceTable); + } + + [Fact] + public async Task LogDayShardingPage() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var page = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(300, page.Total); + + var page1 = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(300, page1.Total); + } + + [Fact] + public async Task Order_Average() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var moneyAverage = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => o.Money).AverageAsync(); + Assert.Equal(105, moneyAverage); + + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("C"); + var sum = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => o.Money).SumAsync(); + Assert.Equal(0, sum); + var sum1 = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (long?)o.Money).SumAsync(); + Assert.Equal(0, sum1); + var sum2 = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (int)o.Money).SumAsync(); + Assert.Equal(0, sum2); + var sum3 = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (int?)o.Money).SumAsync(); + Assert.Equal(0, sum3); + var sum4 = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (decimal)o.Money).SumAsync(); + Assert.Equal(0, sum4); + var sum5 = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (decimal?)o.Money).SumAsync(); + Assert.Equal(0, sum5); + var sum6 = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (double)o.Money).SumAsync(); + Assert.Equal(0, sum6); + var sum7 = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (double?)o.Money).SumAsync(); + Assert.Equal(0, sum7); + var sum8 = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (float)o.Money).SumAsync(); + Assert.Equal(0, sum8); + var sum9 = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (float?)o.Money).SumAsync(); + Assert.Equal(0, sum9); + } + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintDataSource("C"); + var sum = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => o.Money).SumAsync(); + Assert.Equal(0, sum); + } + + var max = await _virtualDbContext.Set().MaxAsync(o => o.Money); + Assert.Equal(319, max); + var all = await _virtualDbContext.Set().AllAsync(o => o.Money <= 321); + Assert.True(all); + var longCount = await _virtualDbContext.Set().LongCountAsync(); + Assert.Equal(320, longCount); + } + + [Fact] + public async Task Order_Max() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + + + var moneyMax = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => o.Money).MaxAsync(); + Assert.Equal(120, moneyMax); + var moneyMax1 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (long?)o.Money).MaxAsync(); + Assert.Equal(120, moneyMax1); + var moneyMax2 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (int)o.Money).MaxAsync(); + Assert.Equal(120, moneyMax2); + var moneyMax3 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (int?)o.Money).MaxAsync(); + Assert.Equal(120, moneyMax3); + var moneyMax4 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (double)o.Money).MaxAsync(); + Assert.Equal(120, moneyMax4); + var moneyMax5 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (double?)o.Money).MaxAsync(); + Assert.Equal(120, moneyMax5); + var moneyMax6 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (float)o.Money).MaxAsync(); + Assert.Equal(120, moneyMax6); + var moneyMax7 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (float?)o.Money).MaxAsync(); + Assert.Equal(120, moneyMax7); + var moneyMax8 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (decimal)o.Money).MaxAsync(); + Assert.Equal(120, moneyMax8); + var moneyMax9 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (decimal?)o.Money).MaxAsync(); + Assert.Equal(120, moneyMax9); + + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("C"); + try + { + var max = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => o.Money).MaxAsync(); + } + catch (Exception e) + { + Assert.True(typeof(InvalidOperationException) == e.GetType() || typeof(TargetInvocationException) == e.GetType()); + Assert.True(e.Message.Contains("contains") || e.InnerException.Message.Contains("contains")); + } + } + } + [Fact] + public async Task Order_Min() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + + + var moneyMin = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => o.Money).MinAsync(); + Assert.Equal(90, moneyMin); + var moneyMin1 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (long?)o.Money).MinAsync(); + Assert.Equal(90, moneyMin1); + var moneyMin2 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (int)o.Money).MinAsync(); + Assert.Equal(90, moneyMin2); + var moneyMin3 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (int?)o.Money).MinAsync(); + Assert.Equal(90, moneyMin3); + var moneyMin4 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (float)o.Money).MinAsync(); + Assert.Equal(90, moneyMin4); + var moneyMin5 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (float?)o.Money).MinAsync(); + Assert.Equal(90, moneyMin5); + var moneyMin6 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (double)o.Money).MinAsync(); + Assert.Equal(90, moneyMin6); + var moneyMin7 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (double?)o.Money).MinAsync(); + Assert.Equal(90, moneyMin7); + var moneyMin8 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (decimal)o.Money).MinAsync(); + Assert.Equal(90, moneyMin8); + var moneyMin9 = await _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (decimal?)o.Money).MinAsync(); + Assert.Equal(90, moneyMin9); + + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("C"); + try + { + var max = await _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => o.Money).MinAsync(); + } + catch (Exception e) + { + Assert.True(typeof(InvalidOperationException) == e.GetType() || typeof(TargetInvocationException) == e.GetType()); + Assert.True(e.Message.Contains("contains") || e.InnerException.Message.Contains("contains")); + } + } + } + + [Fact] + public async Task Order_Entity() + { + var x = await _virtualDbContext.Set().OrderBy(o => o.Money).LastOrDefaultAsync(); + Assert.NotNull(x); + Assert.Equal(319, x.Money); + + var x2 = await _virtualDbContext.Set().Skip(10).OrderBy(o => o.Money).LastOrDefaultAsync(); + Assert.NotNull(x2); + Assert.Equal(319, x2.Money); + + var x3 = await _virtualDbContext.Set().Skip(9999999).OrderBy(o => o.Money).LastOrDefaultAsync(); + Assert.Null(x3); + try + { + var x4 = await _virtualDbContext.Set().Skip(9999999).OrderBy(o => o.Money).LastAsync(); + } + catch (Exception e) + { + Assert.True($"{e}".Contains("Sequence contains no elements.")); + } + + var x1 = await _virtualDbContext.Set().OrderBy(o => o.Money).LastAsync(); + Assert.Equal(x, x1); + var y = await _virtualDbContext.Set().OrderBy(o => o.Money).FirstOrDefaultAsync(); + Assert.NotNull(y); + Assert.Equal(0, y.Money); + var y1 = await _virtualDbContext.Set().OrderBy(o => o.Money).FirstAsync(); + Assert.Equal(y, y1); + var z = await _virtualDbContext.Set().SingleOrDefaultAsync(o => o.Money == 13); + var z1 = await _virtualDbContext.Set().SingleAsync(o => o.Money == 13); + Assert.Equal(z, z1); + } + + [Fact] + public async Task OrderReadWrite() + { + //�л���ֻ�����ݿ⣬ֻ�����ݿ���ֻ������A����Դ��ȡB����Դ + _virtualDbContext.ReadWriteSeparationReadOnly(); + var list = await _virtualDbContext.Set().Where(o => o.Money == 1).ToListAsync(); + Assert.Equal(2, list.Count); + + _virtualDbContext.ReadWriteSeparationWriteOnly(); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("A"); + var areaB = await _virtualDbContext.Set().Where(o => o.Area == "B").FirstOrDefaultAsync(); + Assert.Null(areaB); + } + _virtualDbContext.ReadWriteSeparationReadOnly(); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("A"); + var areaB = await _virtualDbContext.Set().Where(o => o.Area == "B").FirstOrDefaultAsync(); + Assert.NotNull(areaB); + } + + _virtualDbContext.ReadWriteSeparationWriteOnly(); + using (_shardingReadWriteManager.CreateScope()) + { + _shardingReadWriteManager.GetCurrent().SetReadWriteSeparation(100, true); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("A"); + var areaB = await _virtualDbContext.Set().Where(o => o.Area == "B").FirstOrDefaultAsync(); + Assert.NotNull(areaB); + } + } + using (_shardingReadWriteManager.CreateScope()) + { + _shardingReadWriteManager.GetCurrent().SetReadWriteSeparation(100, true); + _virtualDbContext.ReadWriteSeparationWriteOnly(); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("A"); + var areaB = await _virtualDbContext.Set().Where(o => o.Area == "B").FirstOrDefaultAsync(); + Assert.Null(areaB); + } + } + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("A"); + var areaB = await _virtualDbContext.Set().Where(o => o.Area == "B").FirstOrDefaultAsync(); + Assert.Null(areaB); + } + } + + + [Fact] + public async Task LogWeekDateTimeCountTest() + { + var countAsync = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(300, countAsync); + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var fourCount = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).CountAsync(); + Assert.Equal(30, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("20210419_25"); + var countAsync1 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(7, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("20210419_25", "20210426_02"); + var countAsync2 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(14, countAsync2); + } + } + + [Fact] + public async Task LogWeekDateTimeShardingPage() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var page = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(31, page.Total); + + var page1 = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(31, page1.Total); + } + + [Fact] + public async Task LogWeekTimeLongCountTest() + { + var countAsync = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(300, countAsync); + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var fourCount = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).CountAsync(); + Assert.Equal(30, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("20210419_25"); + var countAsync1 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(7, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("20210419_25", "20210426_02"); + var countAsync2 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(14, countAsync2); + } + } + + [Fact] + public async Task LogWeekDateLongShardingPage() + { + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var page = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(31, page.Total); + + var page1 = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(31, page1.Total); + } + + [Fact] + public async Task LogYearTimeLongCountTest() + { + var countAsync = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(600, countAsync); + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var fourCount = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).CountAsync(); + Assert.Equal(30, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("2020"); + var countAsync1 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(366, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("2021"); + var countAsync2 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(234, countAsync2); + } + } + [Fact] + public async Task LogYearDateLongShardingPage() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var page = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(31, page.Total); + + var page1 = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(31, page1.Total); + } + [Fact] + public async Task LogMonthTimeLongCountTest() + { + var countAsync = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(300, countAsync); + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var fourCount = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).CountAsync(); + Assert.Equal(30, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("202104"); + var countAsync1 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(30, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("202104", "202105"); + var countAsync2 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(61, countAsync2); + } + } + [Fact] + public async Task LogMonthDateLongShardingPage() + { + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var page = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(31, page.Total); + + var page1 = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(31, page1.Total); + } + [Fact] + public async Task LogYearLongCountTest() + { + var countAsync = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(300, countAsync); + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var fourCount = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).CountAsync(); + Assert.Equal(30, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("2021"); + var countAsync1 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(300, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + + } + [Fact] + public async Task LogYearLongShardingPage() + { + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var page = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(31, page.Total); + + var page1 = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(31, page1.Total); + } + [Fact] + public async Task CrudTest() + { + var logNoSharding = new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }; + var logNoShardings = new List() + { + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }, + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + } + + }; + var logNoSharding1 = new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }; + var logNoSharding1s = new List() + { + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }, + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + } + + }; + using (var tran = await _virtualDbContext.Database.BeginTransactionAsync()) + { + + try + { + await _virtualDbContext.AddAsync(logNoSharding); + + await _virtualDbContext.AddRangeAsync(logNoShardings); + + await _virtualDbContext.Set().AddAsync(logNoSharding1); + + await _virtualDbContext.Set().AddRangeAsync(logNoSharding1s); + await _virtualDbContext.SaveChangesAsync(); + await tran.CommitAsync(); + } + catch (Exception e) + { + await tran.RollbackAsync(); + } + } + logNoSharding.Body = DateTime.Now.ToString("yyyyMMdd"); + _virtualDbContext.Update(logNoSharding); + + logNoShardings.ForEach(o => o.Body = DateTime.Now.ToString("yyyyMMdd")); + _virtualDbContext.UpdateRange(logNoShardings); + + logNoSharding1.Body = DateTime.Now.ToString("yyyyMMdd"); + _virtualDbContext.Set().Update(logNoSharding1); + + logNoSharding1s.ForEach(o => o.Body = DateTime.Now.ToString("yyyyMMdd")); + _virtualDbContext.Set().UpdateRange(logNoSharding1s); + await _virtualDbContext.SaveChangesAsync(); + + + _virtualDbContext.Remove(logNoSharding); + + _virtualDbContext.RemoveRange(logNoShardings); + + _virtualDbContext.Set().Remove(logNoSharding1); + + logNoSharding1s.ForEach(o => o.Body = DateTime.Now.ToString("yyyyMMdd")); + _virtualDbContext.Set().RemoveRange(logNoSharding1s); + await _virtualDbContext.SaveChangesAsync(); + } + [Fact] + public async Task CrudTest1() + { + var logNoSharding = new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }; + var logNoShardings = new List() + { + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }, + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + } + + }; + using (var tran = await _virtualDbContext.Database.BeginTransactionAsync()) + { + + try + { + await _virtualDbContext.AddAsync((object)logNoSharding); + + await _virtualDbContext.AddRangeAsync(logNoShardings.Select(o => (object)o).ToArray()); + + await _virtualDbContext.SaveChangesAsync(); + await tran.CommitAsync(); + } + catch (Exception e) + { + await tran.RollbackAsync(); + } + } + logNoSharding.Body = DateTime.Now.ToString("yyyyMMdd"); + _virtualDbContext.Update((object)logNoSharding); + + logNoShardings.ForEach(o => o.Body = DateTime.Now.ToString("yyyyMMdd")); + _virtualDbContext.UpdateRange(logNoShardings.Select(o => (object)o).ToArray()); + + await _virtualDbContext.SaveChangesAsync(); + + + _virtualDbContext.Remove((object)logNoSharding); + + _virtualDbContext.RemoveRange(logNoShardings.Select(o => (object)o).ToArray()); + + await _virtualDbContext.SaveChangesAsync(); + } + + + + [Fact] + public async Task Int_ToList_All_Route_Test() + { + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("00"); + + var mod00s = await _virtualDbContext.Set().ToListAsync(); + Assert.Equal(333, mod00s.Count); + } + var mods = await _virtualDbContext.Set().ToListAsync(); + Assert.Equal(1000, mods.Count); + + var modOrders1 = await _virtualDbContext.Set().OrderBy(o => o.Age).ToListAsync(); + int ascAge = 1; + foreach (var sysUserMod in modOrders1) + { + Assert.Equal(ascAge, sysUserMod.Age); + ascAge++; + } + + + var modOrders2 = await _virtualDbContext.Set().OrderByDescending(o => o.Age).ToListAsync(); + int descAge = 1000; + foreach (var sysUserMod in modOrders2) + { + Assert.Equal(descAge, sysUserMod.Age); + descAge--; + } + } + [Fact] + public async Task Int_ToList_All_Test() + { + + var mods = await _virtualDbContext.Set().ToListAsync(); + Assert.Equal(1000, mods.Count); + + var modOrders1 = await _virtualDbContext.Set().OrderBy(o => o.Age).ToListAsync(); + int ascAge = 1; + foreach (var sysUserMod in modOrders1) + { + Assert.Equal(ascAge, sysUserMod.Age); + ascAge++; + } + + var modOrders2 = await _virtualDbContext.Set().OrderByDescending(o => o.Age).ToListAsync(); + int descAge = 1000; + foreach (var sysUserMod in modOrders2) + { + Assert.Equal(descAge, sysUserMod.Age); + descAge--; + } + + + + var pageResult = await _virtualDbContext.Set().Skip(10).Take(10).OrderByDescending(o => o.Age).ToListAsync(); + Assert.Equal(10, pageResult.Count); + int pageDescAge = 990; + foreach (var sysUserMod in pageResult) + { + Assert.Equal(pageDescAge, sysUserMod.Age); + pageDescAge--; + } + } + + + [Fact] + public async Task LogDayLongCountTest() + { + var countAsync = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(3000, countAsync); + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var fourCount = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).CountAsync(); + Assert.Equal(300, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("20210102"); + var countAsync1 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(10, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("20210103", "20210104"); + var countAsync2 = await _virtualDbContext.Set().CountAsync(); + Assert.Equal(20, countAsync2); + } + } + + [Fact] + public async Task LogDayLongShardingPage() + { + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var page = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(300, page.Total); + + var page1 = await _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPageAsync(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(300, page1.Total); + } + + [Fact] + public async Task AsRouteTest() + { + var countAsync3 = await _virtualDbContext.Set().AsRoute(o => + { + o.TryCreateOrAddMustTail("202104"); + }).CountAsync(); + Assert.Equal(30, countAsync3); + var countAsync2 = await _virtualDbContext.Set().AsRoute(o => + { + o.TryCreateOrAddMustTail("2021"); + }).CountAsync(); + Assert.Equal(300, countAsync2); + + var countAsync4 = await _virtualDbContext.Set().AsRoute(o => + { + o.TryCreateOrAddMustTail("20210419_25"); + }).CountAsync(); + Assert.Equal(7, countAsync4); + + var countAsync5 = await _virtualDbContext.Set().AsRoute(o => + { + o.TryCreateOrAddHintTail("20210419_25", "20210426_02"); + }).CountAsync(); + Assert.Equal(14, countAsync5); + + + var fiveBegin = new DateTime(2021, 5, 1).Date; + var sum = await _virtualDbContext.Set().AsRoute(o => + { + o.TryCreateOrAddHintDataSource("C"); + }) + .Where(o => o.CreateTime == fiveBegin).Select(o => o.Money).SumAsync(); + Assert.Equal(0, sum); + } + [Fact] + public async Task QueryInner_Test() + { + var sysUserMods = _virtualDbContext.Set().Select(o => o); + var sysUserModInts = await _virtualDbContext.Set().Where(o => sysUserMods.Select(i => i.Age).Any(i => i == o.Age)).ToListAsync(); + Assert.Equal(1000, sysUserModInts.Count); + } + [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)) + .GroupBy(g => new { UId = g.UserId }) + .Select(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) + }).ToListAsync(); + 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_Test1() + { + 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)) + .GroupBy(g => new { UId = g.UserId }) + .Select(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) + }).OrderBy(o=>o.TotalSalary).ToListAsync(); + Assert.Equal(2, group.Count); + Assert.Equal(2, group[0].Count); + Assert.Equal("200", group[0].GroupUserId); + 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_Test2() + { + 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)) + .GroupBy(g => new { UId = g.UserId }) + .Select(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) + }).OrderByDescending(o=>o.TotalSalary).ToListAsync(); + Assert.Equal(2, group.Count); + Assert.Equal(2, group[0].Count); + Assert.Equal("300", group[0].GroupUserId); + Assert.Equal(2690000, group[0].TotalSalary); + Assert.Equal(1345000, group[0].AvgSalary); + Assert.Equal(13450, group[0].AvgSalaryDecimal); + Assert.Equal(1330000, group[0].MinSalary); + Assert.Equal(1360000, group[0].MaxSalary); + } + + [Fact] + public async Task Group_Recently_Test() + { + //var list =(from us in _virtualDbContext.Set().Where(o => ids.Contains(o.UserId)) + // group us by new + // { + // UserId=us.UserId + // } + // into g + // select new + // { + // UserId=g.Key.UserId, + // DateOfMonth = g.Max(o=>o.DateOfMonth) + // }).ToList(); + //var y = list; + + var ids = new List() { "200", "300" }; + List result = new List(ids.Count); + var routeFilter = new List().AsQueryable().Where(o => ids.Contains(o.UserId)); + var dataSourceRouteResult = new DataSourceRouteResult(new HashSet() { _virtualDataSource.DefaultDataSourceName }); + var tableRouteResults = _tableRouteRuleEngineFactory.Route(dataSourceRouteResult, routeFilter,new Dictionary(){{typeof(SysUserSalary),null}}) + .RouteUnits + .Select(o => o.TableRouteResult.ReplaceTables.First().Tail).OrderByDescending(o => o).ToList(); + foreach (var tableRouteResult in tableRouteResults) + { + if (ids.IsEmpty()) + break; + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail(tableRouteResult); + var queryable = _virtualDbContext.Set().Where(o => ids.Contains(o.UserId)) + .GroupBy(o => new { o.UserId }, i => i, + (i, u) => new + { + Data = u.OrderByDescending(o => o.DateOfMonth).FirstOrDefault() + }); + var r = await queryable.ToListAsync(); + result.AddRange(r.Select(o => o.Data)); + var removeUserIds = result.Select(u => u.UserId).ToHashSet(); + ids.RemoveAll(o => removeUserIds.Contains(o)); + } + } + } + } +} \ No newline at end of file diff --git a/test/ShardingCore.Test6x/ShardingTestSync.cs b/test/ShardingCore.Test6x/ShardingTestSync.cs new file mode 100644 index 00000000..15b0d683 --- /dev/null +++ b/test/ShardingCore.Test6x/ShardingTestSync.cs @@ -0,0 +1,1541 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.QueryRouteManagers.Abstractions; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources.PhysicDataSources; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; +using ShardingCore.Exceptions; +using ShardingCore.Extensions; +using ShardingCore.Extensions.ShardingPageExtensions; +using ShardingCore.Helpers; +using ShardingCore.Sharding; +using ShardingCore.Sharding.ParallelTables; +using ShardingCore.Sharding.ReadWriteConfigurations; +using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions; +using ShardingCore.TableCreator; +using ShardingCore.Test6x.Domain.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using ShardingCore.Core.RuntimeContexts; +using ShardingCore.Core.VirtualRoutes.Abstractions; +using ShardingCore.Extensions.ShardingQueryableExtensions; +using ShardingCore.Sharding.ShardingComparision.Abstractions; +using Xunit; + +namespace ShardingCore.Test6x +{ + + + public class ShardingTestSync + { + private readonly ShardingDefaultDbContext _virtualDbContext; + private readonly IShardingRouteManager _shardingRouteManager; + private readonly ActualConnectionStringManager _connectionStringManager; + private readonly IConfiguration _configuration; + private readonly IEntityMetadataManager _entityMetadataManager; + private readonly IVirtualDataSource _virtualDataSource; + private readonly ITableRouteManager _tableRouteManager; + private readonly IShardingTableCreator _shardingTableCreator; + private readonly IShardingReadWriteManager _shardingReadWriteManager; + private readonly IRouteTailFactory _routeTailFactory; + private readonly IReadWriteConnectorFactory _readWriteConnectorFactory; + private readonly IShardingConnectionStringResolver _shardingConnectionStringResolver; + private readonly IShardingComparer _shardingComparer; + + public ShardingTestSync(ShardingDefaultDbContext virtualDbContext, IShardingRuntimeContext shardingRuntimeContext, IConfiguration configuration) + { + _virtualDbContext = virtualDbContext; + _shardingRouteManager = shardingRuntimeContext.GetShardingRouteManager(); + _shardingReadWriteManager = shardingRuntimeContext.GetShardingReadWriteManager(); + _virtualDataSource = shardingRuntimeContext.GetVirtualDataSource(); + _connectionStringManager = new ActualConnectionStringManager(_shardingReadWriteManager,_virtualDataSource); + _configuration = configuration; + _entityMetadataManager = shardingRuntimeContext.GetEntityMetadataManager(); + _tableRouteManager = shardingRuntimeContext.GetTableRouteManager(); + _shardingTableCreator = shardingRuntimeContext.GetShardingTableCreator(); + _routeTailFactory = shardingRuntimeContext.GetRouteTailFactory(); + _shardingComparer = shardingRuntimeContext.GetShardingComparer(); + _readWriteConnectorFactory = shardingRuntimeContext.GetReadWriteConnectorFactory(); + var readWriteConnectors = _virtualDataSource.ConfigurationParams.ReadWriteNodeSeparationConfigs.Select(o => _readWriteConnectorFactory.CreateConnector(_virtualDataSource.ConfigurationParams.ReadStrategy.GetValueOrDefault(), o.Key, o.Value)); + _shardingConnectionStringResolver = new ReadWriteShardingConnectionStringResolver(readWriteConnectors, _virtualDataSource.ConfigurationParams.ReadStrategy.GetValueOrDefault(),_readWriteConnectorFactory); + } + // [Fact] + // public void RouteParseCompileCacheTest() + // { + // var expressionEqualityComparer = new RouteParseExpressionEqualityComparer(); + // var virtualTable = _virtualTableManager.GetVirtualTable(); + // var virtualTableRoute = (AbstractShardingOperatorVirtualTableRoute)virtualTable.GetVirtualRoute(); + // + // var queryable1 = _virtualDbContext.Set().Where(o => o.DateOfMonth >= 202102); + // var routeParseExpression1 = ShardingUtil.GetRouteParseExpression(queryable1, virtualTableRoute.EntityMetadata, + // (i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true); + // var queryable2 = _virtualDbContext.Set().Where(ox => ox.DateOfMonth >= 202102); + // var routeParseExpression2 = ShardingUtil.GetRouteParseExpression(queryable2, virtualTableRoute.EntityMetadata, + // (i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true); + // var xxxx1 = 202102; + // var queryable3 = _virtualDbContext.Set().Where(ox => ox.DateOfMonth >= xxxx1); + // var routeParseExpression3 = ShardingUtil.GetRouteParseExpression(queryable3, virtualTableRoute.EntityMetadata, + // (i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true); + // var queryable4 = _virtualDbContext.Set().Where(o => o.DateOfMonth >= 202101); + // var routeParseExpression4 = ShardingUtil.GetRouteParseExpression(queryable4, virtualTableRoute.EntityMetadata, + // (i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true); + // var queryable5 = _virtualDbContext.Set().Where(o => o.DateOfMonth > 202101); + // var routeParseExpression5 = ShardingUtil.GetRouteParseExpression(queryable5, virtualTableRoute.EntityMetadata, + // (i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true); + // var queryable6 = _virtualDbContext.Set().Where(o => o.DateOfMonth == 202101); + // var routeParseExpression6 = ShardingUtil.GetRouteParseExpression(queryable6, virtualTableRoute.EntityMetadata, + // (i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true); + // var queryable7 = _virtualDbContext.Set().Where(o => 202101 <= o.DateOfMonth); + // var routeParseExpression7 = ShardingUtil.GetRouteParseExpression(queryable7, virtualTableRoute.EntityMetadata, + // (i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true); + // var queryable8 = _virtualDbContext.Set().Where(o => 202101 == o.DateOfMonth); + // var routeParseExpression8 = ShardingUtil.GetRouteParseExpression(queryable8, virtualTableRoute.EntityMetadata, + // (i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true); + // Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression2)); + // Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression3)); + // Assert.NotEqual(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression4)); + // Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression4), expressionEqualityComparer.GetHashCode(routeParseExpression5)); + // Assert.NotEqual(expressionEqualityComparer.GetHashCode(routeParseExpression5), expressionEqualityComparer.GetHashCode(routeParseExpression6)); + // Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression4), expressionEqualityComparer.GetHashCode(routeParseExpression7)); + // Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression6), expressionEqualityComparer.GetHashCode(routeParseExpression8)); + // + // } + + [Fact] + public void GenericTest() + { + var a = new DefaultPhysicDataSource("aaa", "aaa", true); + var b = new DefaultPhysicDataSource("aaa", "aaa1", false); + Assert.Equal(a, b); + var x = new EntityMetadata(typeof(LogDay)); + //, "aa", typeof(ShardingDefaultDbContext), new List(),null + var y = new EntityMetadata(typeof(LogDay)); + //, "aa1", typeof(ShardingDefaultDbContext), new List(),null + Assert.Equal(x, y); + var dateTime = new DateTime(2021, 1, 1); + var logDays = Enumerable.Range(0, 100).Select(o => new LogDay() { Id = Guid.NewGuid(), LogLevel = "info", LogBody = o.ToString(), LogTime = dateTime.AddDays(o) }).ToList(); + var bulkShardingTableEnumerable = _virtualDbContext.BulkShardingTableEnumerable(logDays); + Assert.Equal(100, bulkShardingTableEnumerable.Count); + var bulkShardingEnumerable = _virtualDbContext.BulkShardingEnumerable(logDays); + Assert.Equal(1, bulkShardingEnumerable.Count); + foreach (var (key, value) in bulkShardingEnumerable) + { + Assert.Equal(100, value.Count); + } + + _virtualDbContext.Set().Where(o => o.Id == "300").ShardingPrint(); + var contains = _virtualDbContext.Set().Where(o => o.Id == "300").Select(o => o.Id).Contains("300"); + Assert.True(contains); + + try + { + var x1 = _virtualDataSource.GetPhysicDataSource("abc"); + } + catch (Exception e) + { + Assert.Equal(typeof(ShardingCoreNotFoundException), e.GetType()); + } + + var queryable = new List().Select(o => new SequenceClass { Id = "123", T = o }).AsQueryable(); + var sourceType = queryable.GetType().GetSequenceType(); + Assert.Equal(typeof(SequenceClass), sourceType); + try + { + _shardingTableCreator.CreateTable("A", "202105"); + } + catch (Exception e) + { + Assert.Equal(typeof(ShardingCoreException), e.GetType()); + } + + var orderMetadata = _entityMetadataManager.TryGet(); + Assert.NotNull(orderMetadata); + var isKey1 = orderMetadata.ShardingDataSourceFieldIsKey(); + Assert.False(isKey1); + var isKey2 = orderMetadata.ShardingTableFieldIsKey(); + Assert.False(isKey2); + var userModMetadata = _entityMetadataManager.TryGet(); + Assert.NotNull(userModMetadata); + var isKey3 = userModMetadata.ShardingDataSourceFieldIsKey(); + Assert.False(isKey3); + var isKey4 = userModMetadata.ShardingTableFieldIsKey(); + Assert.True(isKey4); + + _virtualDbContext.AddRange(logDays); + var bulkShardingExpression = _virtualDbContext.BulkShardingExpression(o => new[] { "A", "B" }.Contains(o.Area)); + Assert.Equal(2, bulkShardingExpression.Count); + Assert.True(bulkShardingExpression.ContainsKey("A")); + Assert.True(bulkShardingExpression.ContainsKey("B")); + + var bulkShardingTableExpression = _virtualDbContext.BulkShardingTableExpression(o => o.Id == Guid.NewGuid().ToString()); + Assert.Equal(1, bulkShardingTableExpression.Count()); + + var isShardingDbContext = _virtualDbContext.IsShardingDbContext(); + Assert.True(isShardingDbContext); + var isShardingTableDbContext = _virtualDbContext.IsShardingTableDbContext(); + Assert.True(isShardingTableDbContext); + var shardingDbContext = _virtualDbContext.GetType().IsShardingDbContext(); + Assert.True(shardingDbContext); + var shardingTableDbContext = _virtualDbContext.GetType().IsShardingTableDbContext(); + Assert.True(shardingTableDbContext); + + var x1x1 = new ParallelTableGroupNode(new HashSet() + { new ParallelTableComparerType(typeof(SysUserMod)), new ParallelTableComparerType(typeof(SysUserSalary)) }); + var x2x2 = new ParallelTableGroupNode(new HashSet() + { new ParallelTableComparerType(typeof(SysUserSalary)),new ParallelTableComparerType(typeof(SysUserMod)), }); + Assert.Equal(x1x1, x2x2); + Assert.Equal(x1x1.GetHashCode(), x2x2.GetHashCode()); + + } + + public class SequenceClass + { + public string Id { get; set; } + public string T { get; set; } + } + + [Fact] + public void TestMultiShardingProperty() + { + + var multiOrder = _virtualDbContext.Set().Where(o => o.Id == 232398109278351360).FirstOrDefault(); + Assert.NotNull(multiOrder); + var longs = new[] { 232398109278351360, 255197859283087360 }; + var multiOrders = _virtualDbContext.Set().Where(o => longs.Contains(o.Id)).ToList(); + Assert.Equal(2, multiOrders.Count); + var dateTime = new DateTime(2021, 11, 1); + var multiOrder404 = _virtualDbContext.Set().Where(o => o.Id == 250345338962063360 && o.CreateTime < dateTime).FirstOrDefault(); + Assert.Null(multiOrder404); + } + [Fact] + public void TestEntityMetadataManager() + { + var objMetadata0 = _entityMetadataManager.TryGet(typeof(object)); + Assert.Null(objMetadata0); + var objMetadata1 = _entityMetadataManager.TryGet(); + Assert.Null(objMetadata1); + + var objMetadata2 = _entityMetadataManager.TryGet(typeof(SysUserMod)); + Assert.NotNull(objMetadata2); + var objMetadata3 = _entityMetadataManager.TryGet(); + Assert.NotNull(objMetadata3); + var sysUserModIsShardingTable0 = _entityMetadataManager.IsShardingTable(typeof(SysUserMod)); + Assert.True(sysUserModIsShardingTable0); + var sysUserModIsShardingTable1 = _entityMetadataManager.IsShardingTable(); + Assert.True(sysUserModIsShardingTable1); + var sysUserModIsShardingDataSource0 = _entityMetadataManager.IsShardingDataSource(typeof(SysUserMod)); + Assert.False(sysUserModIsShardingDataSource0); + var sysUserModIsShardingDataSource1 = _entityMetadataManager.IsShardingDataSource(); + Assert.False(sysUserModIsShardingDataSource1); + } + [Fact] + public void TestShardingComparer() + { + var x = new Guid("7CDE28F8-D548-B96D-1C61-39FFE37AE492"); + var y = new Guid("3425D899-291D-921B-DDE4-49FFE37AE493"); + //asc y 0); + //asc x().Where(o=>o.Id=="339"); + // var routeResults1 = _tableRouteRuleEngineFactory.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 = _tableRouteRuleEngineFactory.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 void ToList_All_Route_Test() + { + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("00"); + + var mod00s = _virtualDbContext.Set().ToList(); + Assert.Equal(333, mod00s.Count); + } + var mods = _virtualDbContext.Set().ToList(); + Assert.Equal(1000, mods.Count); + + var modOrders1 = _virtualDbContext.Set().OrderBy(o => o.Age).ToList(); + int ascAge = 1; + foreach (var sysUserMod in modOrders1) + { + Assert.Equal(ascAge, sysUserMod.Age); + ascAge++; + } + + + var modOrders2 = _virtualDbContext.Set().OrderByDescending(o => o.Age).ToList(); + int descAge = 1000; + foreach (var sysUserMod in modOrders2) + { + Assert.Equal(descAge, sysUserMod.Age); + descAge--; + } + } + [Fact] + public void ToList_All_Test() + { + + var mods = _virtualDbContext.Set().ToList(); + Assert.Equal(1000, mods.Count); + + var modOrders1 = _virtualDbContext.Set().OrderBy(o => o.Age).ToList(); + int ascAge = 1; + foreach (var sysUserMod in modOrders1) + { + Assert.Equal(ascAge, sysUserMod.Age); + ascAge++; + } + + var modOrders2 = _virtualDbContext.Set().OrderByDescending(o => o.Age).ToList(); + int descAge = 1000; + foreach (var sysUserMod in modOrders2) + { + Assert.Equal(descAge, sysUserMod.Age); + descAge--; + } + + + + var pageResult = _virtualDbContext.Set().Skip(10).Take(10).OrderByDescending(o => o.Age).ToList(); + Assert.Equal(10, pageResult.Count); + int pageDescAge = 990; + foreach (var sysUserMod in pageResult) + { + Assert.Equal(pageDescAge, sysUserMod.Age); + pageDescAge--; + } + var skip10First = _virtualDbContext.Set().Skip(10).OrderByDescending(o => o.Age).FirstOrDefault(); + Assert.Equal(skip10First, pageResult[0]); + } + + [Fact] + public void ToList_Join_Test() + { + var list = (from u in _virtualDbContext.Set() + join salary in _virtualDbContext.Set() + on u.Id equals salary.UserId + select new + { + u.Id, + u.Age, + Salary = salary.Salary, + DateOfMonth = salary.DateOfMonth, + Name = u.Name + }).ToList(); + + var list2 = list.OrderBy(o => o.Age).Select(o => o.Age).Distinct().ToList(); + 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 = queryable.ToList(); + Assert.Equal(24, list1.Count()); + Assert.DoesNotContain(list1, o => o.Name != "name_300"); + + var queryable1 = (from u in _virtualDbContext.Set().Where(o => o.Id == "300") + join salary in _virtualDbContext.Set().Where(o => o.DateOfMonth == 202005) + on u.Id equals salary.UserId + select new + { + Salary = salary.Salary, + DateOfMonth = salary.DateOfMonth, + Name = u.Name + }); + var list3 = queryable1.ToList(); + Assert.Equal(1, list3.Count()); + Assert.Contains(list3, o => o.Name == "name_300"); + } + + [Fact] + public void ToList_OrderBy_Asc_Desc_Test() + { + var modascs = _virtualDbContext.Set().OrderBy(o => o.Age).ToList(); + Assert.Equal(1000, modascs.Count); + var i = 1; + foreach (var age in modascs) + { + Assert.Equal(i, age.Age); + i++; + } + + var moddescs = _virtualDbContext.Set().OrderByDescending(o => o.Age).ToList(); + Assert.Equal(1000, moddescs.Count); + var j = 1000; + foreach (var age in moddescs) + { + Assert.Equal(j, age.Age); + j--; + } + } + + [Fact] + public void ToList_Id_In_Test() + { + var ids = new[] { "1", "2", "3", "4" }; + var sysUserMods = _virtualDbContext.Set().Where(o => new List { "1", "2", "3", "4" }.Contains(o.Id)).ToList(); + foreach (var id in ids) + { + Assert.Contains(sysUserMods, o => o.Id == id); + } + + Assert.DoesNotContain(sysUserMods, o => o.Age > 4); + } + + [Fact] + public void ToList_Id_Eq_Test() + { + var id = 3; + var mods = _virtualDbContext.Set().Where(o => o.Id == id.ToString()).ToList(); + Assert.Single(mods); + var mods1 = _virtualDbContext.Set().Where(o => o.Id == "4").ToList(); + Assert.Single(mods1); + Assert.Equal("3", mods[0].Id); + } + + [Fact] + public void ToList_Id_Not_Eq_Test() + { + var mods = _virtualDbContext.Set().Where(o => o.Id != "3").ToList(); + Assert.Equal(999, mods.Count); + Assert.DoesNotContain(mods, o => o.Id == "3"); + } + + [Fact] + public void ToList_Id_Not_Eq_Skip_Test() + { + var mods = _virtualDbContext.Set().Where(o => o.Id != "3").OrderBy(o => o.Age).Skip(2).ToList(); + 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 = _virtualDbContext.Set().Where(o => o.Id != "3").OrderByDescending(o => o.Age).Skip(13).ToList(); + 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 void ToList_Name_Eq_Test() + { + var mods = _virtualDbContext.Set().Where(o => o.Name == "name_3").ToList(); + Assert.Single(mods); + Assert.Equal("3", mods[0].Id); + } + + [Fact] + public void ToList_Id_Eq_Not_In_Db_Test() + { + var mods = _virtualDbContext.Set().Where(o => o.Id == "1001").ToList(); + Assert.Empty(mods); + } + + [Fact] + public void ToList_Name_Eq_Not_In_Db_Test() + { + var mods = _virtualDbContext.Set().Where(o => o.Name == "name_1001").ToList(); + Assert.Empty(mods); + } + + [Fact] + public void FirstOrDefault_Order_By_Id_Test() + { + var sysUserModAge = _virtualDbContext.Set().OrderBy(o => o.Age).FirstOrDefault(); + Assert.True(sysUserModAge != null && sysUserModAge.Id == "1"); + var sysUserModAgeDesc = _virtualDbContext.Set().OrderByDescending(o => o.Age).FirstOrDefault(); + Assert.True(sysUserModAgeDesc != null && sysUserModAgeDesc.Id == "1000"); + var sysUserMod = _virtualDbContext.Set().OrderBy(o => o.Id).FirstOrDefault(); + Assert.True(sysUserMod != null && sysUserMod.Id == "1"); + + var sysUserModDesc = _virtualDbContext.Set().OrderByDescending(o => o.Id).FirstOrDefault(); + Assert.True(sysUserModDesc != null && sysUserModDesc.Id == "999"); + + } + + [Fact] + public void FirstOrDefault2() + { + + var x = new Object[] { "1", "2" }; + var sysUserModab = _virtualDbContext.Set().Where(o => o.Id.Equals("1")).FirstOrDefault(); + Assert.NotNull(sysUserModab); + Assert.True(sysUserModab.Id == "1"); + var sysUserModaa = _virtualDbContext.Set().Where(o => "1".Equals(o.Id)).FirstOrDefault(); + Assert.NotNull(sysUserModaa); + Assert.True(sysUserModaa.Id == "1"); + var sysUserMod = _virtualDbContext.Set().Where(o => o.Id == "1").FirstOrDefault(); + Assert.NotNull(sysUserMod); + Assert.True(sysUserMod.Id == "1"); + var sysUserModxx = _virtualDbContext.Set().Where(o => x.Contains(o.Id)).FirstOrDefault(); + Assert.NotNull(sysUserModxx); + Assert.Equal(sysUserModaa, sysUserMod); + Assert.True(x.Contains(sysUserModxx.Id)); + Assert.NotNull(sysUserMod); + var userMod = _virtualDbContext.Set().Find("1"); + Assert.Equal(sysUserMod, userMod); + Assert.True(sysUserMod.Id == "1"); + var user198 = _virtualDbContext.Set().FirstOrDefault(o => o.Id == "198"); + Assert.True(user198.Id == "198"); + var userId198 = _virtualDbContext.Set().Where(o => o.Id == "198").Select(o => o.Id).FirstOrDefault(); + Assert.Equal(userId198, "198"); + + } + + [Fact] + public void FirstOrDefault3() + { + var sysUserMod = _virtualDbContext.Set().Where(o => o.Name == "name_2").FirstOrDefault(); + Assert.NotNull(sysUserMod); + Assert.Equal("2", sysUserMod.Id); + + } + + [Fact] + public void FirstOrDefault4() + { + var sysUserMod = _virtualDbContext.Set().Where(o => o.Id != "1").FirstOrDefault(); + Assert.NotNull(sysUserMod); + Assert.True(sysUserMod.Id != "1"); + } + + [Fact] + public void FirstOrDefault5() + { + var sysUserMod = _virtualDbContext.Set().Where(o => o.Name == "name_1001").FirstOrDefault(); + Assert.Null(sysUserMod); + } + + [Fact] + public void Count_Test() + { + var a = _virtualDbContext.Set().Where(o => o.Name == "name_1000").Count(); + Assert.Equal(1, a); + var b = _virtualDbContext.Set().Where(o => o.Name != "name_1000").Count(); + Assert.Equal(999, b); + } + + [Fact] + public void Sum_Test() + { + var a = _virtualDbContext.Set().Sum(o => o.Age); + var expected = 0; + for (int i = 1; i <= 1000; i++) + { + expected += i; + } + + Assert.Equal(expected, a); + var b = _virtualDbContext.Set().Where(o => o.Name != "name_1000").Sum(o => o.Age); + Assert.Equal(expected - 1000, b); + } + + [Fact] + public void Max_Test() + { + var a = _virtualDbContext.Set().Max(o => o.Age); + Assert.Equal(1000, a); + var b = _virtualDbContext.Set().Where(o => o.Name != "name_1000").Max(o => o.Age); + Assert.Equal(999, b); + var c = _virtualDbContext.Set().Where(o => o.Age < 500).Max(o => o.Age); + Assert.Equal(499, c); + var e = _virtualDbContext.Set().Where(o => o.Age <= 500).Max(o => o.Age); + Assert.Equal(500, e); + } + + [Fact] + public void 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 = queryable.Max(o => o.Salary); + Assert.Equal(1390000, maxSalary); + } + + [Fact] + public void Min_Test() + { + var a = _virtualDbContext.Set().Min(o => o.Age); + Assert.Equal(1, a); + var b = _virtualDbContext.Set().Where(o => o.Name != "name_1").Min(o => o.Age); + Assert.Equal(2, b); + var c = _virtualDbContext.Set().Where(o => o.Age > 500).Min(o => o.Age); + Assert.Equal(501, c); + var e = _virtualDbContext.Set().Where(o => o.Age >= 500).Min(o => o.Age); + Assert.Equal(500, e); + } + + [Fact] + public void Any_Test() + { + var a = _virtualDbContext.Set().Any(o => o.Age == 100); + Assert.True(a); + var b = _virtualDbContext.Set().Where(o => o.Name != "name_1").Any(o => o.Age == 1); + Assert.False(b); + var c = _virtualDbContext.Set().Where(o => o.Age > 500).Any(o => o.Age <= 500); + Assert.False(c); + var e = _virtualDbContext.Set().Where(o => o.Age >= 500).Any(o => o.Age <= 500); + Assert.True(e); + } + + [Fact] + public void Group_Test() + { + var ids = new[] { "200", "300" }; + var dateOfMonths = new[] { 202111, 202110 }; + var group = (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) + }).ToList(); + 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 void OrderCountTest() + { + var asyncCount = _virtualDbContext.Set().Count(); + Assert.Equal(320, asyncCount); + var syncCount = _virtualDbContext.Set().Count(); + Assert.Equal(320, syncCount); + + var countA = _virtualDbContext.Set().Count(o => o.Area == "A"); + var countB = _virtualDbContext.Set().Count(o => o.Area == "B"); + var countC = _virtualDbContext.Set().Count(o => o.Area == "C"); + Assert.Equal(320, countA + countB + countC); + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var fourCount = _virtualDbContext.Set().Where(o => o.CreateTime >= fourBegin && o.CreateTime < fiveBegin).Count(); + Assert.Equal(30, fourCount); + } + [Fact] + public void OrderFirstTest() + { + var threeMonth = new DateTime(2021, 3, 1); + var order = _virtualDbContext.Set().FirstOrDefault(o => o.CreateTime == threeMonth);//第59条 1月31天2月28天 + Assert.NotNull(order); + Assert.Equal(59, order.Money); + Assert.Equal("C", order.Area); + } + [Fact] + public void OrderOrderTest() + { + var orders = _virtualDbContext.Set().OrderBy(o => o.CreateTime).ToList(); + Assert.Equal(320, orders.Count); + var i = 0; + foreach (var order in orders) + { + Assert.Equal(i, order.Money); + i++; + } + + var threeMonth = new DateTime(2021, 3, 1); + var orderPage = _virtualDbContext.Set().Where(o => o.CreateTime > threeMonth).OrderByDescending(o => o.CreateTime).ToShardingPage(1, 20); + Assert.Equal(20, orderPage.Data.Count); + Assert.Equal(260, orderPage.Total); + + var j = 319; + foreach (var order in orderPage.Data) + { + Assert.Equal(j, order.Money); + j--; + } + } + + [Fact] + public void LogDayCountTest() + { + var countAsync = _virtualDbContext.Set().Count(); + Assert.Equal(3000, countAsync); + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var fourCount = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).Count(); + Assert.Equal(300, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("20210102"); + var countAsync1 = _virtualDbContext.Set().Count(); + Assert.Equal(10, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("20210103", "20210104"); + var countAsync2 = _virtualDbContext.Set().Count(); + Assert.Equal(20, countAsync2); + } + } + [Fact] + public void LogDayTableSeparatorTest() + { + var virtualTableRoute = _tableRouteManager.GetRoute(typeof(LogDay)); + var virtualTableName = virtualTableRoute.EntityMetadata.LogicTableName; + Assert.Equal(nameof(LogDay), virtualTableName); + Assert.True(string.IsNullOrWhiteSpace(virtualTableRoute.EntityMetadata.TableSeparator)); + var entityMetadata = _entityMetadataManager.TryGet(); + Assert.NotNull(entityMetadata); + var isShardingTable = entityMetadata.IsShardingTable(); + Assert.True(isShardingTable); + var isShardingDataSource = entityMetadata.IsShardingDataSource(); + Assert.False(isShardingDataSource); + var emptySeparator = string.IsNullOrWhiteSpace(entityMetadata.TableSeparator); + Assert.True(emptySeparator); + Assert.Null(entityMetadata.AutoCreateDataSourceTable); + } + + [Fact] + public void LogDayShardingPage() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var page = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPage(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(300, page.Total); + + var page1 = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPage(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(300, page1.Total); + + } + + [Fact] + public void Order_Average() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var moneyAverage = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => o.Money).Average(); + Assert.Equal(105, moneyAverage); + + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("C"); + var sum = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => o.Money).Sum(); + Assert.Equal(0, sum); + var sum1 = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (long?)o.Money).Sum(); + Assert.Equal(0, sum1); + var sum2 = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (int)o.Money).Sum(); + Assert.Equal(0, sum2); + var sum3 = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (int?)o.Money).Sum(); + Assert.Equal(0, sum3); + var sum4 = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (decimal)o.Money).Sum(); + Assert.Equal(0, sum4); + var sum5 = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (decimal?)o.Money).Sum(); + Assert.Equal(0, sum5); + var sum6 = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (double)o.Money).Sum(); + Assert.Equal(0, sum6); + var sum7 = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (double?)o.Money).Sum(); + Assert.Equal(0, sum7); + var sum8 = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (float)o.Money).Sum(); + Assert.Equal(0, sum8); + var sum9 = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => (float?)o.Money).Sum(); + Assert.Equal(0, sum9); + } + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintDataSource("C"); + var sum = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => o.Money).Sum(); + Assert.Equal(0, sum); + } + + var all = _virtualDbContext.Set().All(o => o.Money <= 321); + Assert.True(all); + var longCount = _virtualDbContext.Set().LongCount(); + Assert.Equal(320, longCount); + } + + [Fact] + public void Order_Max() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var moneyMax = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => o.Money).Max(); + Assert.Equal(120, moneyMax); + var moneyMax1 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (long?)o.Money).Max(); + Assert.Equal(120, moneyMax1); + var moneyMax2 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (int)o.Money).Max(); + Assert.Equal(120, moneyMax2); + var moneyMax3 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (int?)o.Money).Max(); + Assert.Equal(120, moneyMax3); + var moneyMax4 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (double)o.Money).Max(); + Assert.Equal(120, moneyMax4); + var moneyMax5 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (double?)o.Money).Max(); + Assert.Equal(120, moneyMax5); + var moneyMax6 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (float)o.Money).Max(); + Assert.Equal(120, moneyMax6); + var moneyMax7 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (float?)o.Money).Max(); + Assert.Equal(120, moneyMax7); + var moneyMax8 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (decimal)o.Money).Max(); + Assert.Equal(120, moneyMax8); + var moneyMax9 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (decimal?)o.Money).Max(); + Assert.Equal(120, moneyMax9); + + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("C"); + try + { + var max = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => o.Money).Max(); + } + catch (Exception e) + { + Assert.True(typeof(InvalidOperationException) == e.GetType() || typeof(TargetInvocationException) == e.GetType()); + Assert.True(e.Message.Contains("contains")|| e.InnerException.Message.Contains("contains")); + } + } + } + [Fact] + public void Order_Min() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var moneyMin = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => o.Money).Min(); + Assert.Equal(90, moneyMin); + var moneyMin1 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (long?)o.Money).Min(); + Assert.Equal(90, moneyMin1); + var moneyMin2 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (int)o.Money).Min(); + Assert.Equal(90, moneyMin2); + var moneyMin3 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (int?)o.Money).Min(); + Assert.Equal(90, moneyMin3); + var moneyMin4 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (float)o.Money).Min(); + Assert.Equal(90, moneyMin4); + var moneyMin5 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (float?)o.Money).Min(); + Assert.Equal(90, moneyMin5); + var moneyMin6 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (double)o.Money).Min(); + Assert.Equal(90, moneyMin6); + var moneyMin7 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (double?)o.Money).Min(); + Assert.Equal(90, moneyMin7); + var moneyMin8 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (decimal)o.Money).Min(); + Assert.Equal(90, moneyMin8); + var moneyMin9 = _virtualDbContext.Set() + .Where(o => o.CreateTime >= fourBegin && o.CreateTime <= fiveBegin).Select(o => (decimal?)o.Money).Min(); + Assert.Equal(90, moneyMin9); + + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("C"); + try + { + var max = _virtualDbContext.Set() + .Where(o => o.CreateTime == fiveBegin).Select(o => o.Money).Min(); + } + catch (Exception e) + { + Assert.True(typeof(InvalidOperationException) == e.GetType() || typeof(TargetInvocationException) == e.GetType()); + Assert.True(e.Message.Contains("contains") || e.InnerException.Message.Contains("contains")); + } + } + } + + [Fact] + public void Order_Entity() + { + var x = _virtualDbContext.Set().OrderBy(o => o.Money).LastOrDefault(); + Assert.NotNull(x); + Assert.Equal(319, x.Money); + var x1 = _virtualDbContext.Set().OrderBy(o => o.Money).Last(); + Assert.Equal(x, x1); + var y = _virtualDbContext.Set().OrderBy(o => o.Money).FirstOrDefault(); + Assert.NotNull(y); + Assert.Equal(0, y.Money); + var y1 = _virtualDbContext.Set().OrderBy(o => o.Money).First(); + Assert.Equal(y, y1); + var z = _virtualDbContext.Set().SingleOrDefault(o => o.Money == 13); + var z1 = _virtualDbContext.Set().Single(o => o.Money == 13); + Assert.Equal(z, z1); + } + [Fact] + public void OrderReadWrite() + { + //切换到只读数据库,只读数据库又只配置了A数据源读取B数据源 + _virtualDbContext.ReadWriteSeparationReadOnly(); + var list = _virtualDbContext.Set().Where(o => o.Money == 1).ToList(); + Assert.Equal(2, list.Count); + + _virtualDbContext.ReadWriteSeparationWriteOnly(); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("A"); + var areaB = _virtualDbContext.Set().Where(o => o.Area == "B").FirstOrDefault(); + Assert.Null(areaB); + } + _virtualDbContext.ReadWriteSeparationReadOnly(); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("A"); + var areaB = _virtualDbContext.Set().Where(o => o.Area == "B").FirstOrDefault(); + Assert.NotNull(areaB); + } + _virtualDbContext.ReadWriteSeparationWriteOnly(); + using (_shardingReadWriteManager.CreateScope()) + { + _shardingReadWriteManager.GetCurrent().SetReadWriteSeparation(100, true); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("A"); + var areaB = _virtualDbContext.Set().Where(o => o.Area == "B").FirstOrDefault(); + Assert.NotNull(areaB); + } + } + using (_shardingReadWriteManager.CreateScope()) + { + _shardingReadWriteManager.GetCurrent().SetReadWriteSeparation(100, true); + _virtualDbContext.ReadWriteSeparationWriteOnly(); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("A"); + var areaB = _virtualDbContext.Set().Where(o => o.Area == "B").FirstOrDefault(); + Assert.Null(areaB); + } + } + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustDataSource("A"); + var areaB = _virtualDbContext.Set().Where(o => o.Area == "B").FirstOrDefault(); + Assert.Null(areaB); + } + } + [Fact] + public void LogWeekDateTimeCountTest() + { + var countAsync = _virtualDbContext.Set().Count(); + Assert.Equal(300, countAsync); + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var fourCount = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).Count(); + Assert.Equal(30, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("20210419_25"); + var countAsync1 = _virtualDbContext.Set().Count(); + Assert.Equal(7, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("20210419_25", "20210426_02"); + var countAsync2 = _virtualDbContext.Set().Count(); + Assert.Equal(14, countAsync2); + } + } + + [Fact] + public void LogWeekDateTimeShardingPage() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var page = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPage(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(31, page.Total); + + var page1 = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPage(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(31, page1.Total); + } + + [Fact] + public void LogWeekTimeLongCountTest() + { + var countAsync = _virtualDbContext.Set().Count(); + Assert.Equal(300, countAsync); + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var fourCount = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).Count(); + Assert.Equal(30, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("20210419_25"); + var countAsync1 = _virtualDbContext.Set().Count(); + Assert.Equal(7, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("20210419_25", "20210426_02"); + var countAsync2 = _virtualDbContext.Set().Count(); + Assert.Equal(14, countAsync2); + } + } + + [Fact] + public void LogWeekDateLongShardingPage() + { + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var page = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPage(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(31, page.Total); + + var page1 = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPage(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(31, page1.Total); + } + [Fact] + public void LogYearTimeLongCountTest() + { + var countAsync = _virtualDbContext.Set().Count(); + Assert.Equal(600, countAsync); + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var fourCount = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).Count(); + Assert.Equal(30, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("2020"); + var countAsync1 = _virtualDbContext.Set().Count(); + Assert.Equal(366, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("2021"); + var countAsync2 = _virtualDbContext.Set().Count(); + Assert.Equal(234, countAsync2); + } + } + [Fact] + public void LogYearDateLongShardingPage() + { + var fourBegin = new DateTime(2021, 4, 1).Date; + var fiveBegin = new DateTime(2021, 5, 1).Date; + var page = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPage(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(31, page.Total); + + var page1 = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPage(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(31, page1.Total); + } + + [Fact] + public void LogMonthTimeLongCountTest() + { + var countAsync = _virtualDbContext.Set().Count(); + Assert.Equal(300, countAsync); + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var fourCount = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).Count(); + Assert.Equal(30, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("202104"); + var countAsync1 = _virtualDbContext.Set().Count(); + Assert.Equal(30, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("202104", "202105"); + var countAsync2 = _virtualDbContext.Set().Count(); + Assert.Equal(61, countAsync2); + } + } + [Fact] + public void LogMonthDateLongShardingPage() + { + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var page = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPage(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(31, page.Total); + + var page1 = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPage(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(31, page1.Total); + } + [Fact] + public async Task LogYearLongCountTest() + { + var countAsync = _virtualDbContext.Set().Count(); + Assert.Equal(300, countAsync); + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var fourCount = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).Count(); + Assert.Equal(30, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("2021"); + var countAsync1 = _virtualDbContext.Set().Count(); + Assert.Equal(300, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + } + [Fact] + public async Task LogYearLongShardingPage() + { + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var page = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPage(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(31, page.Total); + + var page1 = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPage(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(31, page1.Total); + } + + [Fact] + public void CrudTest() + { + var logNoSharding = new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }; + var logNoShardings = new List() + { + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }, + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + } + + }; + var logNoSharding1 = new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }; + var logNoSharding1s = new List() + { + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }, + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + } + + }; + using (var tran = _virtualDbContext.Database.BeginTransaction()) + { + + try + { + _virtualDbContext.Add(logNoSharding); + + _virtualDbContext.AddRange(logNoShardings); + + _virtualDbContext.Set().Add(logNoSharding1); + + _virtualDbContext.Set().AddRange(logNoSharding1s); + _virtualDbContext.SaveChanges(); + tran.Commit(); + } + catch (Exception e) + { + tran.Rollback(); + } + } + logNoSharding.Body = DateTime.Now.ToString("yyyyMMdd"); + _virtualDbContext.Update(logNoSharding); + + logNoShardings.ForEach(o => o.Body = DateTime.Now.ToString("yyyyMMdd")); + _virtualDbContext.UpdateRange(logNoShardings); + + logNoSharding1.Body = DateTime.Now.ToString("yyyyMMdd"); + _virtualDbContext.Set().Update(logNoSharding1); + + logNoSharding1s.ForEach(o => o.Body = DateTime.Now.ToString("yyyyMMdd")); + _virtualDbContext.Set().UpdateRange(logNoSharding1s); + _virtualDbContext.SaveChanges(); + + + _virtualDbContext.Remove(logNoSharding); + + _virtualDbContext.RemoveRange(logNoShardings); + + _virtualDbContext.Set().Remove(logNoSharding1); + + logNoSharding1s.ForEach(o => o.Body = DateTime.Now.ToString("yyyyMMdd")); + _virtualDbContext.Set().RemoveRange(logNoSharding1s); + _virtualDbContext.SaveChanges(); + } + [Fact] + public void CrudTest1() + { + var logNoSharding = new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }; + var logNoShardings = new List() + { + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + }, + new LogNoSharding() + { + Id = Guid.NewGuid().ToString("n"), + Body = DateTime.Now.ToString("yyyyMMddHHmmss"), + CreationTime = DateTime.Now + } + + }; + using (var tran = _virtualDbContext.Database.BeginTransaction()) + { + + try + { + _virtualDbContext.Add((object)logNoSharding); + + _virtualDbContext.AddRange(logNoShardings.Select(o => (object)o).ToArray()); + + _virtualDbContext.SaveChanges(); + tran.Commit(); + } + catch (Exception e) + { + tran.Rollback(); + } + } + } + + + [Fact] + public void Int_ToList_All_Route_Test() + { + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("00"); + + var mod00s = _virtualDbContext.Set().ToList(); + Assert.Equal(333, mod00s.Count); + } + var mods = _virtualDbContext.Set().ToList(); + Assert.Equal(1000, mods.Count); + + var modOrders1 = _virtualDbContext.Set().OrderBy(o => o.Age).ToList(); + int ascAge = 1; + foreach (var sysUserMod in modOrders1) + { + Assert.Equal(ascAge, sysUserMod.Age); + ascAge++; + } + + + var modOrders2 = _virtualDbContext.Set().OrderByDescending(o => o.Age).ToList(); + int descAge = 1000; + foreach (var sysUserMod in modOrders2) + { + Assert.Equal(descAge, sysUserMod.Age); + descAge--; + } + } + [Fact] + public void Int_ToList_All_Test() + { + + var mods = _virtualDbContext.Set().ToList(); + Assert.Equal(1000, mods.Count); + + var modOrders1 = _virtualDbContext.Set().OrderBy(o => o.Age).ToList(); + int ascAge = 1; + foreach (var sysUserMod in modOrders1) + { + Assert.Equal(ascAge, sysUserMod.Age); + ascAge++; + } + + var modOrders2 = _virtualDbContext.Set().OrderByDescending(o => o.Age).ToList(); + int descAge = 1000; + foreach (var sysUserMod in modOrders2) + { + Assert.Equal(descAge, sysUserMod.Age); + descAge--; + } + + + + var pageResult = _virtualDbContext.Set().Skip(10).Take(10).OrderByDescending(o => o.Age).ToList(); + Assert.Equal(10, pageResult.Count); + int pageDescAge = 990; + foreach (var sysUserMod in pageResult) + { + Assert.Equal(pageDescAge, sysUserMod.Age); + pageDescAge--; + } + } + + + [Fact] + public void LogDayLongCountTest() + { + var countAsync = _virtualDbContext.Set().Count(); + Assert.Equal(3000, countAsync); + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var fourCount = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime < fiveBegin).Count(); + Assert.Equal(300, fourCount); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddMustTail("20210102"); + var countAsync1 = _virtualDbContext.Set().Count(); + Assert.Equal(10, countAsync1); + } + Assert.Null(_shardingRouteManager.Current); + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.TryCreateOrAddHintTail("20210103", "20210104"); + var countAsync2 = _virtualDbContext.Set().Count(); + Assert.Equal(20, countAsync2); + } + } + + [Fact] + public void LogDayLongShardingPage() + { + var fourBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 4, 1).Date); + var fiveBegin = ShardingCoreHelper.ConvertDateTimeToLong(new DateTime(2021, 5, 1).Date); + var page = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin).OrderBy(o => o.LogTime) + .ToShardingPage(2, 10); + Assert.Equal(10, page.Data.Count); + Assert.Equal(300, page.Total); + + var page1 = _virtualDbContext.Set().Where(o => o.LogTime >= fourBegin && o.LogTime <= fiveBegin) + .ToShardingPage(2, 10); + Assert.Equal(10, page1.Data.Count); + Assert.Equal(300, page1.Total); + } + [Fact] + public void AsRouteTest() + { + var countAsync3 = _virtualDbContext.Set().AsRoute(o => + { + o.TryCreateOrAddMustTail("202104"); + }).Count(); + Assert.Equal(30, countAsync3); + var countAsync2 = _virtualDbContext.Set().AsRoute(o => + { + o.TryCreateOrAddMustTail("2021"); + }).Count(); + Assert.Equal(300, countAsync2); + + var countAsync4 = _virtualDbContext.Set().AsRoute(o => + { + o.TryCreateOrAddMustTail("20210419_25"); + }).Count(); + Assert.Equal(7, countAsync4); + + var countAsync5 = _virtualDbContext.Set().AsRoute(o => + { + o.TryCreateOrAddHintTail("20210419_25", "20210426_02"); + }).Count(); + Assert.Equal(14, countAsync5); + + + var fiveBegin = new DateTime(2021, 5, 1).Date; + var sum = _virtualDbContext.Set().AsRoute(o => + { + o.TryCreateOrAddHintDataSource("C"); + }) + .Where(o => o.CreateTime == fiveBegin).Select(o => o.Money).Sum(); + Assert.Equal(0, sum); + } + [Fact] + public void QueryInner_Test() + { + var sysUserMods = _virtualDbContext.Set().Select(o => o); + var sysUserModInts = _virtualDbContext.Set().Where(o => sysUserMods.Select(i => i.Age).Any(i => i == o.Age)).ToList(); + Assert.Equal(1000, sysUserModInts.Count); + } + [Fact] + public void Group_API_Test() + { + var ids = new[] { "200", "300" }; + var dateOfMonths = new[] { 202111, 202110 }; + var group = _virtualDbContext.Set() + .Where(o => ids.Contains(o.UserId) && dateOfMonths.Contains(o.DateOfMonth)) + .GroupBy(g => new { UId = g.UserId }) + .Select(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) + }).ToList(); + 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 void Group_API_Test1() + { + var ids = new[] { "200", "300" }; + var dateOfMonths = new[] { 202111, 202110 }; + var group = _virtualDbContext.Set() + .Where(o => ids.Contains(o.UserId) && dateOfMonths.Contains(o.DateOfMonth)) + .GroupBy(g => new { UId = g.UserId }) + .Select(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) + }).OrderBy(o => o.TotalSalary).ToList(); + Assert.Equal(2, group.Count); + Assert.Equal(2, group[0].Count); + Assert.Equal("200", group[0].GroupUserId); + 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 void Group_API_Test2() + { + var ids = new[] { "200", "300" }; + var dateOfMonths = new[] { 202111, 202110 }; + var group = _virtualDbContext.Set() + .Where(o => ids.Contains(o.UserId) && dateOfMonths.Contains(o.DateOfMonth)) + .GroupBy(g => new { UId = g.UserId }) + .Select(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) + }).OrderByDescending(o => o.TotalSalary).ToList(); + Assert.Equal(2, group.Count); + Assert.Equal(2, group[0].Count); + Assert.Equal("300", group[0].GroupUserId); + Assert.Equal(2690000, group[0].TotalSalary); + Assert.Equal(1345000, group[0].AvgSalary); + Assert.Equal(13450, group[0].AvgSalaryDecimal); + Assert.Equal(1330000, group[0].MinSalary); + Assert.Equal(1360000, group[0].MaxSalary); + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/LogDayLongVirtualRoute.cs b/test/ShardingCore.Test6x/Shardings/LogDayLongVirtualRoute.cs new file mode 100644 index 00000000..cc73ab2b --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/LogDayLongVirtualRoute.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Helpers; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.VirtualRoutes.Days; + +namespace ShardingCore.Test6x.Shardings +{ + public class LogDayLongVirtualRoute:AbstractSimpleShardingDayKeyLongVirtualTableRoute + { + //public override bool? EnableRouteParseCompileCache => true; + protected override bool EnableHintRoute => true; + + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.LogTime); + } + + public override bool AutoCreateTableByTime() + { + return true; + } + + public override DateTime GetBeginTime() + { + return new DateTime(2021, 1, 1); + } + + protected override List CalcTailsOnStart() + { + var beginTime = GetBeginTime().Date; + + var tails = new List(); + //提前创建表 + var nowTimeStamp = new DateTime(2021,11,20).Date; + if (beginTime > nowTimeStamp) + throw new ArgumentException("begin time error"); + var currentTimeStamp = beginTime; + while (currentTimeStamp <= nowTimeStamp) + { + var currentTimeStampLong = ShardingCoreHelper.ConvertDateTimeToLong(currentTimeStamp); + var tail = TimeFormatToTail(currentTimeStampLong); + tails.Add(tail); + currentTimeStamp = currentTimeStamp.AddDays(1); + } + return tails; + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/LogDayVirtualTableRoute.cs b/test/ShardingCore.Test6x/Shardings/LogDayVirtualTableRoute.cs new file mode 100644 index 00000000..ac6fb443 --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/LogDayVirtualTableRoute.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Sharding.PaginationConfigurations; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.Test6x.Shardings.PaginationConfigs; +using ShardingCore.VirtualRoutes.Days; + +namespace ShardingCore.Test6x.Shardings +{ + public class LogDayVirtualTableRoute:AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute + { + //public override bool? EnableRouteParseCompileCache => true; + protected override bool EnableHintRoute => true; + + public override DateTime GetBeginTime() + { + return new DateTime(2021, 1, 1); + } + + + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.LogTime); + builder.TableSeparator(string.Empty); + } + + public override IPaginationConfiguration CreatePaginationConfiguration() + { + return new LogDayPaginationConfiguration(); + } + + public override bool AutoCreateTableByTime() + { + return true; + } + + protected override List CalcTailsOnStart() + { + var beginTime = GetBeginTime().Date; + + var tails = new List(); + //提前创建表 + var nowTimeStamp = new DateTime(2021,11,20).Date; + if (beginTime > nowTimeStamp) + throw new ArgumentException("begin time error"); + var currentTimeStamp = beginTime; + while (currentTimeStamp <= nowTimeStamp) + { + var tail = ShardingKeyToTail(currentTimeStamp); + tails.Add(tail); + currentTimeStamp = currentTimeStamp.AddDays(1); + } + return tails; + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/LogMonthLongvirtualRoute.cs b/test/ShardingCore.Test6x/Shardings/LogMonthLongvirtualRoute.cs new file mode 100644 index 00000000..14f75f8f --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/LogMonthLongvirtualRoute.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.Test6x.Domain.Maps; +using ShardingCore.VirtualRoutes.Months; + +namespace ShardingCore.Test6x.Shardings +{ + public class LogMonthLongvirtualRoute:AbstractSimpleShardingMonthKeyLongVirtualTableRoute + { + //public override bool? EnableRouteParseCompileCache => true; + protected override bool EnableHintRoute => true; + + public override bool AutoCreateTableByTime() + { + return true; + } + + public override DateTime GetBeginTime() + { + return new DateTime(2021, 1, 1); + } + + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.LogTime); + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/LogWeekDateTimeVirtualTableRoute.cs b/test/ShardingCore.Test6x/Shardings/LogWeekDateTimeVirtualTableRoute.cs new file mode 100644 index 00000000..6546714b --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/LogWeekDateTimeVirtualTableRoute.cs @@ -0,0 +1,28 @@ +using System; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.VirtualRoutes.Weeks; + +namespace ShardingCore.Test6x.Shardings +{ + public class LogWeekDateTimeVirtualTableRoute:AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute + { + //public override bool? EnableRouteParseCompileCache => true; + protected override bool EnableHintRoute => true; + + public override bool AutoCreateTableByTime() + { + return true; + } + + public override DateTime GetBeginTime() + { + return new DateTime(2021, 1, 1); + } + + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.LogTime); + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/LogWeekTimeLongVirtualTableRoute.cs b/test/ShardingCore.Test6x/Shardings/LogWeekTimeLongVirtualTableRoute.cs new file mode 100644 index 00000000..07e9a5b2 --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/LogWeekTimeLongVirtualTableRoute.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.VirtualRoutes.Weeks; + +namespace ShardingCore.Test6x.Shardings +{ + public class LogWeekTimeLongVirtualTableRoute : AbstractSimpleShardingWeekKeyLongVirtualTableRoute + { + //public override bool? EnableRouteParseCompileCache => true; + protected override bool EnableHintRoute => true; + + public override bool AutoCreateTableByTime() + { + return true; + } + + public override DateTime GetBeginTime() + { + return new DateTime(2021, 1, 1); + } + + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.LogTime); + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/LogYearDateTimeVirtualRoute.cs b/test/ShardingCore.Test6x/Shardings/LogYearDateTimeVirtualRoute.cs new file mode 100644 index 00000000..3ef470aa --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/LogYearDateTimeVirtualRoute.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.VirtualRoutes.Years; + +namespace ShardingCore.Test6x.Shardings +{ + public class LogYearDateTimeVirtualRoute:AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute + { + //public override bool? EnableRouteParseCompileCache => true; + protected override bool EnableHintRoute => true; + + public override bool AutoCreateTableByTime() + { + return true; + } + + public override DateTime GetBeginTime() + { + return new DateTime(2020, 1, 1); + } + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.LogTime); + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/LogYearLongVirtualRoute.cs b/test/ShardingCore.Test6x/Shardings/LogYearLongVirtualRoute.cs new file mode 100644 index 00000000..f07cf3e5 --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/LogYearLongVirtualRoute.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.VirtualRoutes.Years; + +namespace ShardingCore.Test6x.Shardings +{ + public class LogYearLongVirtualRoute:AbstractSimpleShardingYearKeyLongVirtualTableRoute + { + //public override bool? EnableRouteParseCompileCache => true; + protected override bool EnableHintRoute => true; + + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.LogTime); + } + + public override bool AutoCreateTableByTime() + { + return true; + } + + public override DateTime GetBeginTime() + { + return new DateTime(2021, 1, 1); + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/MultiShardingOrderVirtualTableRoute.cs b/test/ShardingCore.Test6x/Shardings/MultiShardingOrderVirtualTableRoute.cs new file mode 100644 index 00000000..0f84419b --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/MultiShardingOrderVirtualTableRoute.cs @@ -0,0 +1,79 @@ +using System; +using System.Linq.Expressions; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Helpers; +using ShardingCore.Test6x.Common; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.VirtualRoutes.Months; + +namespace ShardingCore.Test6x.Shardings +{ + public class MultiShardingOrderVirtualTableRoute:AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute + { + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.CreateTime); + builder.ShardingExtraProperty(o => o.Id); + } + + public override Func GetExtraRouteFilter(object shardingKey, ShardingOperatorEnum shardingOperator, string shardingPropertyName) + { + switch (shardingPropertyName) + { + case nameof(MultiShardingOrder.Id): return GetIdRouteFilter(shardingKey, shardingOperator); + default: throw new NotImplementedException(shardingPropertyName); + } + } + + private Func GetIdRouteFilter(object shardingKey, + ShardingOperatorEnum shardingOperator) + { + //解析雪花id 需要考虑异常情况,传入的可能不是雪花id那么可以随机查询一张表 + var analyzeIdToDateTime = SnowflakeId.AnalyzeIdToDateTime(Convert.ToInt64(shardingKey)); + //当前时间的tail + var t = TimeFormatToTail(analyzeIdToDateTime); + //因为是按月分表所以获取下个月的时间判断id是否是在灵界点创建的 + var nextMonthFirstDay = ShardingCoreHelper.GetNextMonthFirstDay(DateTime.Now); + if (analyzeIdToDateTime.AddSeconds(10) > nextMonthFirstDay) + { + var nextT = TimeFormatToTail(nextMonthFirstDay); + + if (shardingOperator == ShardingOperatorEnum.Equal) + { + return tail => tail == t||tail== nextT; + } + } + var currentMonthFirstDay = ShardingCoreHelper.GetCurrentMonthFirstDay(DateTime.Now); + if (analyzeIdToDateTime.AddSeconds(-10) < currentMonthFirstDay) + { + //上个月tail + var nextT = TimeFormatToTail(analyzeIdToDateTime.AddSeconds(-10)); + + if (shardingOperator == ShardingOperatorEnum.Equal) + { + return tail => tail == t || tail == nextT; + } + } + else + { + if (shardingOperator == ShardingOperatorEnum.Equal) + { + return tail => tail == t; + } + } + + return tail => true; + } + + public override bool AutoCreateTableByTime() + { + return true; + } + + public override DateTime GetBeginTime() + { + return new DateTime(2021, 9, 1); + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/OrderAreaShardingVirtualDataSourceRoute.cs b/test/ShardingCore.Test6x/Shardings/OrderAreaShardingVirtualDataSourceRoute.cs new file mode 100644 index 00000000..a9751977 --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/OrderAreaShardingVirtualDataSourceRoute.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Shardings +{ + public class OrderAreaShardingVirtualDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRoute + { + //public override bool? EnableRouteParseCompileCache => true; + protected override bool EnableHintRoute =>true; + + private readonly List _dataSources = new List() + { + "A", "B", "C" + }; + //我们设置区域就是数据库 + public override string ShardingKeyToDataSourceName(object shardingKey) + { + return shardingKey?.ToString() ?? string.Empty; + } + + public override List GetAllDataSourceNames() + { + return _dataSources; + } + + public override bool AddDataSourceName(string dataSourceName) + { + if (_dataSources.Any(o => o == dataSourceName)) + return false; + _dataSources.Add(dataSourceName); + return true; + } + + public override void Configure(EntityMetadataDataSourceBuilder builder) + { + builder.ShardingProperty(o => o.Area); + } + + public override Func GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator) + { + + var t = ShardingKeyToDataSourceName(shardingKey); + switch (shardingOperator) + { + case ShardingOperatorEnum.Equal: return tail => tail == t; + default: + { + return tail => true; + } + } + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/OrderCreateTimeVirtualTableRoute.cs b/test/ShardingCore.Test6x/Shardings/OrderCreateTimeVirtualTableRoute.cs new file mode 100644 index 00000000..231fc09b --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/OrderCreateTimeVirtualTableRoute.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Sharding.PaginationConfigurations; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.VirtualRoutes.Months; + +namespace ShardingCore.Test6x.Shardings +{ + public class OrderCreateTimeVirtualTableRoute:AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute + { + //public override bool? EnableRouteParseCompileCache => true; + public override DateTime GetBeginTime() + { + return new DateTime(2021, 1, 1); + } + + protected override List CalcTailsOnStart() + { + var allTails = base.CalcTailsOnStart(); + allTails.Add("202112"); + return allTails; + } + + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.CreateTime); + } + + public override IPaginationConfiguration CreatePaginationConfiguration() + { + return new OrderCreateTimePaginationConfiguration(); + } + public override bool AutoCreateTableByTime() + { + return true; + } + } + //public class HistoryMinCompare : IComparer + //{ + // private const string History = "History"; + + // public int Compare(string? x, string? y) + // { + // if (!Object.Equals(x, y)) + // { + // if (History.Equals(x)) + // return -1; + // if (History.Equals(y)) + // return 1; + // } + // return Comparer.Default.Compare(x, y); + // } + //} + + public class OrderCreateTimePaginationConfiguration : IPaginationConfiguration + { + public void Configure(PaginationBuilder builder) + { + builder.PaginationSequence(o => o.CreateTime) + .UseQueryMatch(PaginationMatchEnum.Owner | PaginationMatchEnum.Named | PaginationMatchEnum.PrimaryMatch) + .UseAppendIfOrderNone().UseRouteComparer(Comparer.Default); + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/PaginationConfigs/LogDayPaginationConfiguration.cs b/test/ShardingCore.Test6x/Shardings/PaginationConfigs/LogDayPaginationConfiguration.cs new file mode 100644 index 00000000..66e56cd8 --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/PaginationConfigs/LogDayPaginationConfiguration.cs @@ -0,0 +1,15 @@ +using ShardingCore.Sharding.PaginationConfigurations; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Shardings.PaginationConfigs +{ + public class LogDayPaginationConfiguration: IPaginationConfiguration + { + public void Configure(PaginationBuilder builder) + { + builder.PaginationSequence(o => o.LogTime) + .UseQueryMatch(PaginationMatchEnum.Named | PaginationMatchEnum.Owner | + PaginationMatchEnum.PrimaryMatch).UseAppendIfOrderNone(); + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/SysUserModIntVirtualRoute.cs b/test/ShardingCore.Test6x/Shardings/SysUserModIntVirtualRoute.cs new file mode 100644 index 00000000..36f43d1c --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/SysUserModIntVirtualRoute.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.VirtualRoutes.Mods; + +namespace ShardingCore.Test6x.Shardings +{ + public class SysUserModIntVirtualRoute:AbstractSimpleShardingModKeyIntVirtualTableRoute + { + protected override bool EnableHintRoute => true; + //public override bool? EnableRouteParseCompileCache => true; + + public SysUserModIntVirtualRoute() : base(2, 3) + { + } + + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.Id); + } + } +} diff --git a/test/ShardingCore.Test6x/Shardings/SysUserModVirtualTableRoute.cs b/test/ShardingCore.Test6x/Shardings/SysUserModVirtualTableRoute.cs new file mode 100644 index 00000000..260d3b1c --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/SysUserModVirtualTableRoute.cs @@ -0,0 +1,28 @@ +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.VirtualRoutes.Mods; + +namespace ShardingCore.Test6x.Shardings +{ + /* + * @Author: xjm + * @Description: + * @Date: Thursday, 14 January 2021 15:39:27 + * @Email: 326308290@qq.com + */ + public class SysUserModVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualTableRoute + { + protected override bool EnableHintRoute => true; + //public override bool? EnableRouteParseCompileCache => true; + + public SysUserModVirtualTableRoute() : base(2,3) + { + } + + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.Id); + builder.TableSeparator("_"); + } + } +} \ No newline at end of file diff --git a/test/ShardingCore.Test6x/Shardings/SysUserSalaryVirtualTableRoute.cs b/test/ShardingCore.Test6x/Shardings/SysUserSalaryVirtualTableRoute.cs new file mode 100644 index 00000000..2fb4f1c2 --- /dev/null +++ b/test/ShardingCore.Test6x/Shardings/SysUserSalaryVirtualTableRoute.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions; +using ShardingCore.Test6x.Domain.Entities; + +namespace ShardingCore.Test6x.Shardings +{ +/* +* @Author: xjm +* @Description: +* @Date: Monday, 01 February 2021 15:54:55 +* @Email: 326308290@qq.com +*/ + public class SysUserSalaryVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute + { + //public override bool? EnableRouteParseCompileCache => true; + protected override bool EnableHintRoute => true; + + public override string ShardingKeyToTail(object shardingKey) + { + var time = Convert.ToInt32(shardingKey); + return TimeFormatToTail(time); + } + + public override List GetTails() + { + 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}"; + } + + public override Func 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; + } + } + } + + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.DateOfMonth); + } + } +} \ No newline at end of file diff --git a/test/ShardingCore.Test6x/Startup.cs b/test/ShardingCore.Test6x/Startup.cs new file mode 100644 index 00000000..0bd13cbd --- /dev/null +++ b/test/ShardingCore.Test6x/Startup.cs @@ -0,0 +1,382 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using ShardingCore.Bootstrappers; +using ShardingCore.Core; +using ShardingCore.Helpers; +using ShardingCore.Sharding.ReadWriteConfigurations; +using ShardingCore.TableExists; +using ShardingCore.TableExists.Abstractions; +using ShardingCore.Test6x.Domain.Entities; +using ShardingCore.Test6x.Shardings; + +namespace ShardingCore.Test6x +{ + /* + * @Author: xjm + * @Description: + * @Date: Friday, 15 January 2021 15:37:46 + * @Email: 326308290@qq.com + */ + public class Startup + { + public static readonly ILoggerFactory efLogger = LoggerFactory.Create(builder => + { + builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole(); + }); + + // 支持的形式: + // ConfigureServices(IServiceCollection services) + // ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext) + // ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services) + public void ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext) + { + services.AddShardingDbContext() + .UseRouteConfig(op => + { + op.AddShardingDataSourceRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + + }) + .UseConfig(op => + { + //op.UseEntityFrameworkCoreProxies = true; + //当无法获取路由时会返回默认值而不是报错 + op.ThrowIfQueryRouteNotMatch = false; + //忽略建表错误compensate table和table creator + op.IgnoreCreateTableError = true; + //迁移时使用的并行线程数(分库有效)defaultShardingDbContext.Database.Migrate() + op.MigrationParallelCount = Environment.ProcessorCount; + //补偿表创建并行线程数 调用UseAutoTryCompensateTable有效 + op.CompensateTableParallelCount = Environment.ProcessorCount; + //最大连接数限制 + op.MaxQueryConnectionsLimit = Environment.ProcessorCount; + //链接模式系统默认 + op.ConnectionMode = ConnectionModeEnum.SYSTEM_AUTO; + //如何通过字符串查询创建DbContext + op.UseShardingQuery((conStr, builder) => + { + builder.UseSqlServer(conStr).UseLoggerFactory(efLogger); + }); + //如何通过事务创建DbContext + op.UseShardingTransaction((connection, builder) => + { + builder.UseSqlServer(connection).UseLoggerFactory(efLogger); + }); + //添加默认数据源 + op.AddDefaultDataSource("A", + "Data Source=localhost;Initial Catalog=ShardingCoreDBA;Integrated Security=True;"); + //添加额外数据源 + op.AddExtraDataSource(sp => + { + return new Dictionary() + { + { "B", "Data Source=localhost;Initial Catalog=ShardingCoreDBB;Integrated Security=True;" }, + { "C", "Data Source=localhost;Initial Catalog=ShardingCoreDBC;Integrated Security=True;" }, + }; + }); + //添加读写分离 + op.AddReadWriteSeparation(sp => + { + return new Dictionary>() + { + { + "A", new HashSet() + { + "Data Source=localhost;Initial Catalog=ShardingCoreDBB;Integrated Security=True;" + } + } + }; + }, ReadStrategyEnum.Loop, defaultEnable: false, readConnStringGetStrategy: ReadConnStringGetStrategyEnum.LatestEveryTime); + }) + .ReplaceService() + .AddShardingCore(); + } + + // 可以添加要用到的方法参数,会自动从注册的服务中获取服务实例,类似于 asp.net core 里 Configure 方法 + public void Configure(IServiceProvider serviceProvider) + { + //启动ShardingCore创建表任务 + //启动进行表补偿 + serviceProvider.UseAutoTryCompensateTable(); + // 有一些测试数据要初始化可以放在这里 + InitData(serviceProvider).GetAwaiter().GetResult(); + } + + /// + /// 添加种子数据 + /// + /// + /// + private async Task InitData(IServiceProvider serviceProvider) + { + using (var scope = serviceProvider.CreateScope()) + { + var virtualDbContext = scope.ServiceProvider.GetService(); + if (!await virtualDbContext.Set().AnyAsync()) + { + var ids = Enumerable.Range(1, 1000); + var userMods = new List(); + var userModInts = 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) + }); + userModInts.Add(new SysUserModInt() + { + Id = id, + 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++; + } + } + + var areas = new List(){"A","B","C"}; + List orders = new List(360); + var begin = new DateTime(2021, 1, 1); + for (int i = 0; i < 320; i++) + { + orders.Add(new Order() + { + Id = Guid.NewGuid(), + Area = areas[i%3], + CreateTime = begin, + Money = i + }); + begin = begin.AddDays(1); + } + + List logDays = new List(3600); + List logDayLongs = new List(3600); + + var levels = new List(){"info","warning","error"}; + var begin1 = new DateTime(2021, 1, 1); + for (int i = 0; i < 300; i++) + { + var ltime = begin1; + for (int j = 0; j < 10; j++) + { + logDays.Add(new LogDay() + { + Id = Guid.NewGuid(), + LogLevel = levels[j%3], + LogBody = $"{i}_{j}", + LogTime = ltime.AddHours(1) + }); + logDayLongs.Add(new LogDayLong() + { + Id = Guid.NewGuid(), + LogLevel = levels[j%3], + LogBody = $"{i}_{j}", + LogTime = ShardingCoreHelper.ConvertDateTimeToLong(ltime.AddHours(1)) + }); + ltime = ltime.AddHours(1); + } + begin1 = begin1.AddDays(1); + } + + List logWeeks = new List(300); + var begin2 = new DateTime(2021,1,1); + for (int i = 0; i < 300; i++) + { + logWeeks.Add(new LogWeekDateTime() + { + Id = Guid.NewGuid().ToString("n"), + Body = $"body_{i}", + LogTime = begin2 + }); + begin2 = begin2.AddDays(1); + } + List logWeekLongs = new List(300); + var begin3 = new DateTime(2021,1,1); + for (int i = 0; i < 300; i++) + { + logWeekLongs.Add(new LogWeekTimeLong() + { + Id = Guid.NewGuid().ToString("n"), + Body = $"body_{i}", + LogTime = ShardingCoreHelper.ConvertDateTimeToLong(begin3) + }); + begin3 = begin3.AddDays(1); + } + List logYears = new List(600); + var begin4 = new DateTime(2020,1,1); + for (int i = 0; i < 600; i++) + { + logYears.Add(new LogYearDateTime() + { + Id = Guid.NewGuid(), + LogBody = $"body_{i}", + LogTime = begin4 + }); + begin4 = begin4.AddDays(1); + } + + + List logMonthLongs = new List(300); + var begin5 = new DateTime(2021, 1, 1); + for (int i = 0; i < 300; i++) + { + logMonthLongs.Add(new LogMonthLong() + { + Id = Guid.NewGuid().ToString("n"), + Body = $"body_{i}", + LogTime = ShardingCoreHelper.ConvertDateTimeToLong(begin5) + }); + begin5 = begin5.AddDays(1); + } + + List logYearkLongs = new List(300); + var begin6 = new DateTime(2021, 1, 1); + for (int i = 0; i < 300; i++) + { + logYearkLongs.Add(new LogYearLong() + { + Id = Guid.NewGuid().ToString("n"), + LogBody = $"body_{i}", + LogTime = ShardingCoreHelper.ConvertDateTimeToLong(begin6) + }); + begin6 = begin6.AddDays(1); + } + var multiShardingOrders = new List(9); + #region 添加多字段分表 + + { + var now = new DateTime(2021, 10, 1, 13, 13, 11); + multiShardingOrders.Add(new MultiShardingOrder() + { + Id = 231765457240207360, + Name = $"{now:yyyy/MM/dd HH:mm:ss}", + CreateTime = now + }); + } + { + var now = new DateTime(2021, 10, 2, 11, 3, 11); + multiShardingOrders.Add(new MultiShardingOrder() + { + Id = 232095129534607360, + Name = $"{now:yyyy/MM/dd HH:mm:ss}", + CreateTime = now + }); + } + { + var now = new DateTime(2021, 10, 3, 7, 7, 7); + multiShardingOrders.Add(new MultiShardingOrder() + { + Id = 232398109278351360, + Name = $"{now:yyyy/MM/dd HH:mm:ss}", + CreateTime = now + }); + } + { + var now = new DateTime(2021, 11, 6, 13, 13, 11); + multiShardingOrders.Add(new MultiShardingOrder() + { + Id = 244811420401807360, + Name = $"{now:yyyy/MM/dd HH:mm:ss}", + CreateTime = now + }); + } + { + var now = new DateTime(2021, 11, 21, 19, 43, 0); + multiShardingOrders.Add(new MultiShardingOrder() + { + Id = 250345338962063360, + Name = $"{now:yyyy/MM/dd HH:mm:ss}", + CreateTime = now + }); + } + { + var now = new DateTime(2021, 12, 5, 5, 5, 11); + multiShardingOrders.Add(new MultiShardingOrder() + { + Id = 255197859283087360, + Name = $"{now:yyyy/MM/dd HH:mm:ss}", + CreateTime = now + }); + } + { + var now = new DateTime(2021, 12, 9, 19, 13, 11); + multiShardingOrders.Add(new MultiShardingOrder() + { + Id = 256860816933007360, + Name = $"{now:yyyy/MM/dd HH:mm:ss}", + CreateTime = now + }); + } + { + var now = new DateTime(2021, 12, 19, 13, 13, 11); + multiShardingOrders.Add(new MultiShardingOrder() + { + Id = 260394098622607360, + Name = $"{now:yyyy/MM/dd HH:mm:ss}", + CreateTime = now + }); + } + #endregion + using (var tran = virtualDbContext.Database.BeginTransaction()) + { + await virtualDbContext.AddRangeAsync(userMods); + await virtualDbContext.AddRangeAsync(userModInts); + await virtualDbContext.AddRangeAsync(userSalaries); + await virtualDbContext.AddRangeAsync(orders); + await virtualDbContext.AddRangeAsync(logDays); + await virtualDbContext.AddRangeAsync(logDayLongs); + await virtualDbContext.AddRangeAsync(logWeeks); + await virtualDbContext.AddRangeAsync(logWeekLongs); + await virtualDbContext.AddRangeAsync(logYears); + await virtualDbContext.AddRangeAsync(logMonthLongs); + await virtualDbContext.AddRangeAsync(logYearkLongs); + await virtualDbContext.AddRangeAsync(multiShardingOrders); + + await virtualDbContext.SaveChangesAsync(); + tran.Commit(); + } + } + } + } + } +} \ No newline at end of file