From 26f177aebb24b9f0ffdc73c413b2962e32b17775 Mon Sep 17 00:00:00 2001 From: xuejiaming <326308290@qq.com> Date: Tue, 21 Sep 2021 11:33:41 +0800 Subject: [PATCH] =?UTF-8?q?efcore2x=20=E4=B9=9F=E5=B7=B2=E7=BB=8F=E5=85=A8?= =?UTF-8?q?=E9=9D=A2=E6=94=AF=E6=8C=81=E5=88=86=E5=BA=93=20=E5=90=8E?= =?UTF-8?q?=E7=BB=AD=E5=BC=80=E5=8F=91=E9=92=88=E5=AF=B9=E5=88=86=E5=BA=93?= =?UTF-8?q?=E7=9A=84route=E5=92=8C=E5=AE=8C=E6=88=90=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=9A=84readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ShardingCore.sln | 16 + samples/Sample.BulkConsole/Program.cs | 8 +- .../Sample.SqlServer3x/DefaultDbContext.cs | 2 +- .../AbstractShardingAbpDbContext.cs | 1188 ++++++++--------- .../DbContexts/DefaultShardingDbContext.cs | 1 - .../Jobs/AutoCreateTableByDay.cs | 51 +- .../Samples.AutoByDate.SqlServer/Startup.cs | 22 +- .../Core/PhysicTables/DefaultPhysicTable.cs | 19 +- .../VirtualDatabase/ShardingEntityConfig.cs | 2 +- .../VirtualTables/DefaultVirtualTable.cs | 13 +- .../VirtualTables/IVirtualTable.cs | 5 +- .../VirtualTables/ShardingTableConfig.cs | 72 +- .../IVirtualDataSourceRoute.cs | 2 +- .../DataSourceRouteRuleEngine.cs | 2 +- ...stractShardingOperatorVirtualTableRoute.cs | 6 +- .../EFCores/ShardingDbSetSource.cs | 55 +- .../EFCores/ShardingInternalDbQuery.cs | 25 + .../EFCores/ShardingInternalDbSet.cs | 10 + .../EFCores/ShardingModelCustomizer.cs | 6 +- .../EFCores/ShardingQueryCompiler.cs | 32 + .../ShardingDataSourceNotFoundException.cs | 30 + .../ShardingOwnerNotFoundException.cs | 30 - .../Extensions/CommonExtension.cs | 2 +- .../Extensions/DbContextExtension.cs | 3 + .../Extensions/ShardingExtension.cs | 185 +-- .../Sharding/AbstractShardingDbContext.cs | 24 +- .../IShardingDbContextExecutor.cs | 8 +- .../Abstractions/IShardingQueryExecutor.cs | 4 + ...MemoryReverseStreamMergeAsyncEnumerator.cs | 25 +- ...ggregateOrderStreamMergeAsyncEnumerator.cs | 22 + .../MultiOrderStreamMergeAsyncEnumerator.cs | 13 + .../OrderStreamMergeAsyncEnumerator.cs | 12 + .../PaginationStreamMergeAsyncEnumerator.cs | 18 + .../StreamMergeAsyncEnumerator.cs | 66 + .../Sharding/ShardingDbContextExecutor.cs | 7 + .../DefaultShardingQueryExecutor.cs | 3 + .../IShardingTransaction.cs | 9 +- .../ShardingTransaction.cs | 76 +- .../AsyncEnumerableStreamMergeEngine.cs | 12 +- ...bstractEnumeratorAsyncStreamMergeEngine.cs | 24 +- .../AbstractEnumeratorStreamMergeEngine.cs | 16 +- .../Visitors/QueryableRouteDiscoverVisitor.cs | 13 +- .../ShardingDbContextBootstrapper.cs | 10 +- src/ShardingCore/Utils/ShardingKeyUtil.cs | 138 +- src/ShardingCore/Utils/ShardingUtil.cs | 64 +- src2x/ShardingCore.2x/ShardingCore.2x.csproj | 36 + .../DefaultDbContext.cs | 2 +- test/ShardingCore.Test50/Startup.cs | 2 +- .../Configs/DbConfig.json | 5 + .../DefaultDbContext.cs | 31 + .../Domain/Entities/SysUserMod.cs | 29 + .../Domain/Entities/SysUserSalary.cs | 42 + .../Domain/Maps/SysUserModMap.cs | 23 + .../Domain/Maps/SysUserSalaryMap.cs | 23 + .../ShardingCore.Test50_2x.csproj | 31 + .../ShardingDefaultDbContext.cs | 29 + test/ShardingCore.Test50_2x/ShardingTest.cs | 417 ++++++ .../Shardings/SysUserModVirtualTableRoute.cs | 20 + .../SysUserSalaryVirtualTableRoute.cs | 75 ++ test/ShardingCore.Test50_2x/Startup.cs | 125 ++ .../DefaultDbContext.cs | 2 +- .../ShardingDefaultDbContext.cs | 1 - test/ShardingCore.Test50_3x/Startup.cs | 15 +- 63 files changed, 2292 insertions(+), 967 deletions(-) create mode 100644 src/ShardingCore/EFCores/ShardingInternalDbQuery.cs create mode 100644 src/ShardingCore/Exceptions/ShardingDataSourceNotFoundException.cs delete mode 100644 src/ShardingCore/Exceptions/ShardingOwnerNotFoundException.cs create mode 100644 src2x/ShardingCore.2x/ShardingCore.2x.csproj create mode 100644 test/ShardingCore.Test50_2x/Configs/DbConfig.json create mode 100644 test/ShardingCore.Test50_2x/DefaultDbContext.cs create mode 100644 test/ShardingCore.Test50_2x/Domain/Entities/SysUserMod.cs create mode 100644 test/ShardingCore.Test50_2x/Domain/Entities/SysUserSalary.cs create mode 100644 test/ShardingCore.Test50_2x/Domain/Maps/SysUserModMap.cs create mode 100644 test/ShardingCore.Test50_2x/Domain/Maps/SysUserSalaryMap.cs create mode 100644 test/ShardingCore.Test50_2x/ShardingCore.Test50_2x.csproj create mode 100644 test/ShardingCore.Test50_2x/ShardingDefaultDbContext.cs create mode 100644 test/ShardingCore.Test50_2x/ShardingTest.cs create mode 100644 test/ShardingCore.Test50_2x/Shardings/SysUserModVirtualTableRoute.cs create mode 100644 test/ShardingCore.Test50_2x/Shardings/SysUserSalaryVirtualTableRoute.cs create mode 100644 test/ShardingCore.Test50_2x/Startup.cs diff --git a/ShardingCore.sln b/ShardingCore.sln index ba5993d6..a1a01a8f 100644 --- a/ShardingCore.sln +++ b/ShardingCore.sln @@ -33,6 +33,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShardingCore.Test50_3x", "t EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.BulkConsole", "samples\Sample.BulkConsole\Sample.BulkConsole.csproj", "{2443CC8B-FB7D-47A7-9663-F3848BB30A36}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src2x", "src2x", "{F91949B0-02D5-4E3B-ACF4-AFA6C99A1E04}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.2x", "src2x\ShardingCore.2x\ShardingCore.2x.csproj", "{A07C597D-339D-4378-BE4C-A2AF7473340B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.Test50_2x", "test\ShardingCore.Test50_2x\ShardingCore.Test50_2x.csproj", "{E4DAA43A-B64D-45CF-81B8-7B8FD338D686}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -83,6 +89,14 @@ Global {2443CC8B-FB7D-47A7-9663-F3848BB30A36}.Debug|Any CPU.Build.0 = Debug|Any CPU {2443CC8B-FB7D-47A7-9663-F3848BB30A36}.Release|Any CPU.ActiveCfg = Release|Any CPU {2443CC8B-FB7D-47A7-9663-F3848BB30A36}.Release|Any CPU.Build.0 = Release|Any CPU + {A07C597D-339D-4378-BE4C-A2AF7473340B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A07C597D-339D-4378-BE4C-A2AF7473340B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A07C597D-339D-4378-BE4C-A2AF7473340B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A07C597D-339D-4378-BE4C-A2AF7473340B}.Release|Any CPU.Build.0 = Release|Any CPU + {E4DAA43A-B64D-45CF-81B8-7B8FD338D686}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E4DAA43A-B64D-45CF-81B8-7B8FD338D686}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E4DAA43A-B64D-45CF-81B8-7B8FD338D686}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E4DAA43A-B64D-45CF-81B8-7B8FD338D686}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -99,6 +113,8 @@ Global {1136B8C9-3539-42FA-97FD-CAA6F146FCF0} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73} {C0A59BB0-F0B8-4AC6-B192-0249E784FC88} = {CC2C88C0-65F2-445D-BE78-973B840FE281} {2443CC8B-FB7D-47A7-9663-F3848BB30A36} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73} + {A07C597D-339D-4378-BE4C-A2AF7473340B} = {F91949B0-02D5-4E3B-ACF4-AFA6C99A1E04} + {E4DAA43A-B64D-45CF-81B8-7B8FD338D686} = {CC2C88C0-65F2-445D-BE78-973B840FE281} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8C07A667-E8B4-43C7-8053-721584BAD291} diff --git a/samples/Sample.BulkConsole/Program.cs b/samples/Sample.BulkConsole/Program.cs index 20f0e353..1b95d084 100644 --- a/samples/Sample.BulkConsole/Program.cs +++ b/samples/Sample.BulkConsole/Program.cs @@ -25,7 +25,7 @@ namespace Sample.BulkConsole services.AddLogging(); services.AddShardingDbContext( o => o.UseSqlServer("Data Source=localhost;Initial Catalog=MyOrderSharding;Integrated Security=True;")) - .Begin(true) + .Begin(true,true) .AddShardingQuery((conStr, builder) => builder.UseSqlServer(conStr).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)) .AddShardingTransaction((connection, builder) => builder.UseSqlServer(connection)) .AddDefaultDataSource("ds0", "Data Source=localhost;Initial Catalog=MyOrderSharding;Integrated Security=True;") @@ -64,7 +64,11 @@ namespace Sample.BulkConsole startNew.Restart(); foreach (var keyValuePair in bulkShardingEnumerable) { - keyValuePair.Key.BulkInsert(keyValuePair.Value.ToList()); + foreach (var valuePair in keyValuePair.Value) + { + valuePair.Key.BulkInsert(valuePair.Value.ToList()); + } + } startNew.Stop(); Console.WriteLine($"订单总数:{i}条,myShardingDbContext.BulkInsert(orders)用时:{startNew.ElapsedMilliseconds}毫秒"); diff --git a/samples/Sample.SqlServer3x/DefaultDbContext.cs b/samples/Sample.SqlServer3x/DefaultDbContext.cs index 1323235e..2e333b55 100644 --- a/samples/Sample.SqlServer3x/DefaultDbContext.cs +++ b/samples/Sample.SqlServer3x/DefaultDbContext.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Sample.SqlServer3x.Domain.Maps; -using ShardingCore.Core.VirtualRoutes.RouteTails.Abstractions; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; using ShardingCore.Sharding.Abstractions; namespace Sample.SqlServer3x diff --git a/samples/Samples.AbpSharding/AbstractShardingAbpDbContext.cs b/samples/Samples.AbpSharding/AbstractShardingAbpDbContext.cs index 1812d453..156705d7 100644 --- a/samples/Samples.AbpSharding/AbstractShardingAbpDbContext.cs +++ b/samples/Samples.AbpSharding/AbstractShardingAbpDbContext.cs @@ -1,594 +1,594 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Data.Common; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; -using System.Threading.Tasks; -using Abp.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using Microsoft.EntityFrameworkCore.Storage; -using ShardingCore; -using ShardingCore.Core; -using ShardingCore.Core.VirtualRoutes.RouteTails.Abstractions; -using ShardingCore.Core.VirtualRoutes.TableRoutes; -using ShardingCore.Core.VirtualTables; -using ShardingCore.DbContexts; -using ShardingCore.DbContexts.ShardingDbContexts; -using ShardingCore.Exceptions; -using ShardingCore.Extensions; -using ShardingCore.Sharding.Abstractions; - -namespace Samples.AbpSharding -{ - public abstract class AbstractShardingAbpDbContext : AbpDbContext, IShardingDbContext where T : AbpDbContext, IShardingTableDbContext - { - - - private readonly ConcurrentDictionary _dbContextCaches = new ConcurrentDictionary(); - private readonly IVirtualTableManager _virtualTableManager; - private readonly IRouteTailFactory _routeTailFactory; - private readonly IShardingDbContextFactory _shardingDbContextFactory; - private readonly IShardingDbContextOptionsBuilderConfig _shardingDbContextOptionsBuilderConfig; - private DbContextOptions _dbContextOptions; - - private readonly object CREATELOCK = new object(); - - public AbstractShardingAbpDbContext(DbContextOptions options) : base(options) - { - _shardingDbContextFactory = ShardingContainer.GetService(); - _virtualTableManager = ShardingContainer.GetService(); - _routeTailFactory = ShardingContainer.GetService(); - _shardingDbContextOptionsBuilderConfig = ShardingContainer - .GetService>() - .FirstOrDefault(o => o.ShardingDbContextType == ShardingDbContextType); - } - - public abstract Type ShardingDbContextType { get; } - public Type ActualDbContextType => typeof(T); - - - private DbContextOptionsBuilder CreateDbContextOptionBuilder() - { - Type type = typeof(DbContextOptionsBuilder<>); - type = type.MakeGenericType(ActualDbContextType); - return (DbContextOptionsBuilder) Activator.CreateInstance(type); - } - - private DbContextOptions CreateShareDbContextOptions() - { - var dbContextOptionBuilder = CreateDbContextOptionBuilder(); - var dbConnection = Database.GetDbConnection(); - _shardingDbContextOptionsBuilderConfig.UseDbContextOptionsBuilder(dbConnection, dbContextOptionBuilder); - return dbContextOptionBuilder.Options; - } - private DbContextOptions CreateMonopolyDbContextOptions() - { - var dbContextOptionBuilder = CreateDbContextOptionBuilder(); - var connectionString = Database.GetConnectionString(); - _shardingDbContextOptionsBuilderConfig.UseDbContextOptionsBuilder(connectionString,dbContextOptionBuilder); - return dbContextOptionBuilder.Options; - } - - private ShardingDbContextOptions GetShareShardingDbContextOptions(IRouteTail routeTail) - { - if (_dbContextOptions == null) - { - lock (CREATELOCK) - { - if (_dbContextOptions == null) - { - _dbContextOptions = CreateShareDbContextOptions(); - } - } - } - - return new ShardingDbContextOptions(_dbContextOptions, routeTail); - } - private ShardingDbContextOptions CetMonopolyShardingDbContextOptions(IRouteTail routeTail) - { - return new ShardingDbContextOptions(CreateMonopolyDbContextOptions(), routeTail); - } - - - public DbContext GetDbContext(bool track, IRouteTail routeTail) - { - if (track) - { - if (routeTail.IsMultiEntityQuery()) - throw new ShardingCoreException("multi route not support track"); - if(!(routeTail is ISingleQueryRouteTail singleQueryRouteTail)) - throw new ShardingCoreException("multi route not support track"); - var cacheKey = routeTail.GetRouteTailIdentity(); - if (!_dbContextCaches.TryGetValue(cacheKey, out var dbContext)) - { - dbContext = _shardingDbContextFactory.Create(ShardingDbContextType, GetShareShardingDbContextOptions(routeTail)); - _dbContextCaches.TryAdd(cacheKey, dbContext); - } - - return dbContext; - } - else - { - return _shardingDbContextFactory.Create(ShardingDbContextType, CetMonopolyShardingDbContextOptions(routeTail)); - } - } - - public bool IsBeginTransaction => Database.CurrentTransaction != null; - - public DbContext CreateGenericDbContext(T entity) where T : class - { - var tail = string.Empty; - if (entity.IsShardingTable()) - { - var physicTable = _virtualTableManager.GetVirtualTable(ShardingDbContextType, entity.GetType()).RouteTo(new TableRouteConfig(null, entity as IShardingTable, null))[0]; - tail = physicTable.Tail; - } - - return GetDbContext(true, _routeTailFactory.Create(tail)); - } - - public IEnumerable CreateExpressionDbContext(Expression> where) - where TEntity : class - { - if (typeof(TEntity).IsShardingTable()) - { - var physicTable = _virtualTableManager.GetVirtualTable(ShardingDbContextType, typeof(TEntity)).RouteTo(new TableRouteConfig(predicate:where)); - if (physicTable.IsEmpty()) - throw new ShardingCoreException($"{where.ShardingPrint()} cant found any physic table"); - return physicTable.Select(o => GetDbContext(true, _routeTailFactory.Create(o.Tail))); - } - else - { - return new[] {GetDbContext(true, _routeTailFactory.Create(string.Empty))}; - } - } - - public string GetConnectionString() - { - throw new NotImplementedException(); - } - - public void UseShardingTransaction(DbTransaction transaction) - { - throw new NotImplementedException(); - } - - public override EntityEntry Add(object entity) - { - return CreateGenericDbContext(entity).Add(entity); - } - - public override EntityEntry Add(TEntity entity) - { - return CreateGenericDbContext(entity).Add(entity); - } - - - public override ValueTask> AddAsync(TEntity entity, CancellationToken cancellationToken = new CancellationToken()) - { - return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); - } - - public override ValueTask AddAsync(object entity, CancellationToken cancellationToken = new CancellationToken()) - { - return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); - } - - public override void AddRange(params object[] entities) - { - var groups = entities.Select(o => - { - var dbContext = CreateGenericDbContext(o); - return new - { - DbContext = dbContext, - Entity = o - }; - }).GroupBy(g => g.DbContext); - - foreach (var group in groups) - { - group.Key.AddRange(group.Select(o => o.Entity)); - } - } - - public override void AddRange(IEnumerable entities) - { - var groups = entities.Select(o => - { - var dbContext = CreateGenericDbContext(o); - return new - { - DbContext = dbContext, - Entity = o - }; - }).GroupBy(g => g.DbContext); - - foreach (var group in groups) - { - group.Key.AddRange(group.Select(o => o.Entity)); - } - } - - public override async Task AddRangeAsync(params object[] entities) - { - var groups = entities.Select(o => - { - var dbContext = CreateGenericDbContext(o); - return new - { - DbContext = dbContext, - Entity = o - }; - }).GroupBy(g => g.DbContext); - - foreach (var group in groups) - { - await group.Key.AddRangeAsync(group.Select(o => o.Entity)); - } - } - - public override async Task AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = new CancellationToken()) - { - var groups = entities.Select(o => - { - var dbContext = CreateGenericDbContext(o); - return new - { - DbContext = dbContext, - Entity = o - }; - }).GroupBy(g => g.DbContext); - - foreach (var group in groups) - { - await group.Key.AddRangeAsync(group.Select(o => o.Entity)); - } - } - - public override EntityEntry Attach(TEntity entity) - { - return CreateGenericDbContext(entity).Attach(entity); - } - - public override EntityEntry Attach(object entity) - { - return CreateGenericDbContext(entity).Attach(entity); - } - - public override void AttachRange(params object[] entities) - { - var groups = entities.Select(o => - { - var dbContext = CreateGenericDbContext(o); - return new - { - DbContext = dbContext, - Entity = o - }; - }).GroupBy(g => g.DbContext); - - foreach (var group in groups) - { - group.Key.AttachRange(group.Select(o => o.Entity)); - } - } - - public override void AttachRange(IEnumerable entities) - { - var groups = entities.Select(o => - { - var dbContext = CreateGenericDbContext(o); - return new - { - DbContext = dbContext, - Entity = o - }; - }).GroupBy(g => g.DbContext); - - foreach (var group in groups) - { - group.Key.AttachRange(group.Select(o => o.Entity)); - } - } - - - //public override DatabaseFacade Database => _dbContextCaches.Any() - // ? _dbContextCaches.First().Value.Database - // : GetDbContext(true, string.Empty).Database; - - public override EntityEntry Entry(TEntity entity) - { - return CreateGenericDbContext(entity).Entry(entity); - } - - public override EntityEntry Entry(object entity) - { - return CreateGenericDbContext(entity).Entry(entity); - } - - public override EntityEntry Update(TEntity entity) - { - return CreateGenericDbContext(entity).Update(entity); - } - - public override EntityEntry Update(object entity) - { - return CreateGenericDbContext(entity).Update(entity); - } - - public override void UpdateRange(params object[] entities) - { - var groups = entities.Select(o => - { - var dbContext = CreateGenericDbContext(o); - return new - { - DbContext = dbContext, - Entity = o - }; - }).GroupBy(g => g.DbContext); - - foreach (var group in groups) - { - group.Key.UpdateRange(group.Select(o => o.Entity)); - } - } - - public override void UpdateRange(IEnumerable entities) - { - var groups = entities.Select(o => - { - var dbContext = CreateGenericDbContext(o); - return new - { - DbContext = dbContext, - Entity = o - }; - }).GroupBy(g => g.DbContext); - - foreach (var group in groups) - { - group.Key.UpdateRange(group.Select(o => o.Entity)); - } - } - - public override EntityEntry Remove(TEntity entity) - { - return CreateGenericDbContext(entity).Remove(entity); - } - - public override EntityEntry Remove(object entity) - { - return CreateGenericDbContext(entity).Remove(entity); - } - - public override void RemoveRange(params object[] entities) - { - var groups = entities.Select(o => - { - var dbContext = CreateGenericDbContext(o); - return new - { - DbContext = dbContext, - Entity = o - }; - }).GroupBy(g => g.DbContext); - - foreach (var group in groups) - { - group.Key.RemoveRange(group.Select(o => o.Entity)); - } - } - - public override void RemoveRange(IEnumerable entities) - { - var groups = entities.Select(o => - { - var dbContext = CreateGenericDbContext(o); - return new - { - DbContext = dbContext, - Entity = o - }; - }).GroupBy(g => g.DbContext); - - foreach (var group in groups) - { - group.Key.RemoveRange(group.Select(o => o.Entity)); - } - } - - public override int SaveChanges() - { - var isBeginTransaction = IsBeginTransaction; - //如果是内部开的事务就内部自己消化 - if (!isBeginTransaction) - { - Database.BeginTransaction(); - } - - int i = 0; - - try - { - foreach (var dbContextCache in _dbContextCaches) - { - dbContextCache.Value.Database.UseTransaction(Database.CurrentTransaction.GetDbTransaction()); - i += dbContextCache.Value.SaveChanges(); - } - - if (!isBeginTransaction) - Database.CurrentTransaction.Commit(); - } - finally - { - if (!isBeginTransaction) - { - Database.CurrentTransaction?.Dispose(); - foreach (var dbContextCache in _dbContextCaches) - { - dbContextCache.Value.Database.UseTransaction(null); - } - } - } - - return i; - } - - public override int SaveChanges(bool acceptAllChangesOnSuccess) - { - var isBeginTransaction = IsBeginTransaction; - //如果是内部开的事务就内部自己消化 - if (!isBeginTransaction) - { - Database.BeginTransaction(); - } - - int i = 0; - - try - { - foreach (var dbContextCache in _dbContextCaches) - { - dbContextCache.Value.Database.UseTransaction(Database.CurrentTransaction.GetDbTransaction()); - i += dbContextCache.Value.SaveChanges(acceptAllChangesOnSuccess); - } - - if (!isBeginTransaction) - Database.CurrentTransaction.Commit(); - } - finally - { - if (!isBeginTransaction) - { - Database.CurrentTransaction?.Dispose(); - foreach (var dbContextCache in _dbContextCaches) - { - dbContextCache.Value.Database.UseTransaction(null); - } - } - } - - return i; - } - - - public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) - { - var isBeginTransaction = IsBeginTransaction; - //如果是内部开的事务就内部自己消化 - if (!isBeginTransaction) - { - await Database.BeginTransactionAsync(cancellationToken); - } - - int i = 0; - - try - { - foreach (var dbContextCache in _dbContextCaches) - { - await dbContextCache.Value.Database.UseTransactionAsync(Database.CurrentTransaction.GetDbTransaction(), cancellationToken: cancellationToken); - i += await dbContextCache.Value.SaveChangesAsync(cancellationToken); - } - - if (!isBeginTransaction) - await Database.CurrentTransaction.CommitAsync(cancellationToken); - } - finally - { - if (!isBeginTransaction) - { - } - - if (Database.CurrentTransaction != null) - { - await Database.CurrentTransaction.DisposeAsync(); - foreach (var dbContextCache in _dbContextCaches) - { - await dbContextCache.Value.Database.UseTransactionAsync(null, cancellationToken: cancellationToken); - } - } - } - - return i; - } - - public override async Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken()) - { - var isBeginTransaction = IsBeginTransaction; - //如果是内部开的事务就内部自己消化 - if (!isBeginTransaction) - { - await Database.BeginTransactionAsync(cancellationToken); - } - - int i = 0; - - try - { - foreach (var dbContextCache in _dbContextCaches) - { - await dbContextCache.Value.Database.UseTransactionAsync(Database.CurrentTransaction.GetDbTransaction(), cancellationToken: cancellationToken); - i += await dbContextCache.Value.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); - } - - if (!isBeginTransaction) - await Database.CurrentTransaction.CommitAsync(cancellationToken); - } - finally - { - if (!isBeginTransaction) - if (Database.CurrentTransaction != null) - { - await Database.CurrentTransaction.DisposeAsync(); - - foreach (var dbContextCache in _dbContextCaches) - { - await dbContextCache.Value.Database.UseTransactionAsync(null, cancellationToken: cancellationToken); - } - } - } - - return i; - } - - public override void Dispose() - { - foreach (var dbContextCache in _dbContextCaches) - { - try - { - dbContextCache.Value.Dispose(); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - - base.Dispose(); - } - - public override async ValueTask DisposeAsync() - { - foreach (var dbContextCache in _dbContextCaches) - { - try - { - await dbContextCache.Value.DisposeAsync(); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - - await base.DisposeAsync(); - } - } -} \ No newline at end of file +//using System; +//using System.Collections.Concurrent; +//using System.Collections.Generic; +//using System.Data.Common; +//using System.Linq; +//using System.Linq.Expressions; +//using System.Threading; +//using System.Threading.Tasks; +//using Abp.EntityFrameworkCore; +//using Microsoft.EntityFrameworkCore; +//using Microsoft.EntityFrameworkCore.ChangeTracking; +//using Microsoft.EntityFrameworkCore.Storage; +//using ShardingCore; +//using ShardingCore.Core; +//using ShardingCore.Core.VirtualRoutes.RouteTails.Abstractions; +//using ShardingCore.Core.VirtualRoutes.TableRoutes; +//using ShardingCore.Core.VirtualTables; +//using ShardingCore.DbContexts; +//using ShardingCore.DbContexts.ShardingDbContexts; +//using ShardingCore.Exceptions; +//using ShardingCore.Extensions; +//using ShardingCore.Sharding.Abstractions; + +//namespace Samples.AbpSharding +//{ +// public abstract class AbstractShardingAbpDbContext : AbpDbContext, IShardingDbContext where T : AbpDbContext, IShardingTableDbContext +// { + + +// private readonly ConcurrentDictionary _dbContextCaches = new ConcurrentDictionary(); +// private readonly IVirtualTableManager _virtualTableManager; +// private readonly IRouteTailFactory _routeTailFactory; +// private readonly IShardingDbContextFactory _shardingDbContextFactory; +// private readonly IShardingDbContextOptionsBuilderConfig _shardingDbContextOptionsBuilderConfig; +// private DbContextOptions _dbContextOptions; + +// private readonly object CREATELOCK = new object(); + +// public AbstractShardingAbpDbContext(DbContextOptions options) : base(options) +// { +// _shardingDbContextFactory = ShardingContainer.GetService(); +// _virtualTableManager = ShardingContainer.GetService(); +// _routeTailFactory = ShardingContainer.GetService(); +// _shardingDbContextOptionsBuilderConfig = ShardingContainer +// .GetService>() +// .FirstOrDefault(o => o.ShardingDbContextType == ShardingDbContextType); +// } + +// public abstract Type ShardingDbContextType { get; } +// public Type ActualDbContextType => typeof(T); + + +// private DbContextOptionsBuilder CreateDbContextOptionBuilder() +// { +// Type type = typeof(DbContextOptionsBuilder<>); +// type = type.MakeGenericType(ActualDbContextType); +// return (DbContextOptionsBuilder) Activator.CreateInstance(type); +// } + +// private DbContextOptions CreateShareDbContextOptions() +// { +// var dbContextOptionBuilder = CreateDbContextOptionBuilder(); +// var dbConnection = Database.GetDbConnection(); +// _shardingDbContextOptionsBuilderConfig.UseDbContextOptionsBuilder(dbConnection, dbContextOptionBuilder); +// return dbContextOptionBuilder.Options; +// } +// private DbContextOptions CreateMonopolyDbContextOptions() +// { +// var dbContextOptionBuilder = CreateDbContextOptionBuilder(); +// var connectionString = Database.GetConnectionString(); +// _shardingDbContextOptionsBuilderConfig.UseDbContextOptionsBuilder(connectionString,dbContextOptionBuilder); +// return dbContextOptionBuilder.Options; +// } + +// private ShardingDbContextOptions GetShareShardingDbContextOptions(IRouteTail routeTail) +// { +// if (_dbContextOptions == null) +// { +// lock (CREATELOCK) +// { +// if (_dbContextOptions == null) +// { +// _dbContextOptions = CreateShareDbContextOptions(); +// } +// } +// } + +// return new ShardingDbContextOptions(_dbContextOptions, routeTail); +// } +// private ShardingDbContextOptions CetMonopolyShardingDbContextOptions(IRouteTail routeTail) +// { +// return new ShardingDbContextOptions(CreateMonopolyDbContextOptions(), routeTail); +// } + + +// public DbContext GetDbContext(bool track, IRouteTail routeTail) +// { +// if (track) +// { +// if (routeTail.IsMultiEntityQuery()) +// throw new ShardingCoreException("multi route not support track"); +// if(!(routeTail is ISingleQueryRouteTail singleQueryRouteTail)) +// throw new ShardingCoreException("multi route not support track"); +// var cacheKey = routeTail.GetRouteTailIdentity(); +// if (!_dbContextCaches.TryGetValue(cacheKey, out var dbContext)) +// { +// dbContext = _shardingDbContextFactory.Create(ShardingDbContextType, GetShareShardingDbContextOptions(routeTail)); +// _dbContextCaches.TryAdd(cacheKey, dbContext); +// } + +// return dbContext; +// } +// else +// { +// return _shardingDbContextFactory.Create(ShardingDbContextType, CetMonopolyShardingDbContextOptions(routeTail)); +// } +// } + +// public bool IsBeginTransaction => Database.CurrentTransaction != null; + +// public DbContext CreateGenericDbContext(T entity) where T : class +// { +// var tail = string.Empty; +// if (entity.IsShardingTable()) +// { +// var physicTable = _virtualTableManager.GetVirtualTable(ShardingDbContextType, entity.GetType()).RouteTo(new TableRouteConfig(null, entity as IShardingTable, null))[0]; +// tail = physicTable.Tail; +// } + +// return GetDbContext(true, _routeTailFactory.Create(tail)); +// } + +// public IEnumerable CreateExpressionDbContext(Expression> where) +// where TEntity : class +// { +// if (typeof(TEntity).IsShardingTable()) +// { +// var physicTable = _virtualTableManager.GetVirtualTable(ShardingDbContextType, typeof(TEntity)).RouteTo(new TableRouteConfig(predicate:where)); +// if (physicTable.IsEmpty()) +// throw new ShardingCoreException($"{where.ShardingPrint()} cant found any physic table"); +// return physicTable.Select(o => GetDbContext(true, _routeTailFactory.Create(o.Tail))); +// } +// else +// { +// return new[] {GetDbContext(true, _routeTailFactory.Create(string.Empty))}; +// } +// } + +// public string GetConnectionString() +// { +// throw new NotImplementedException(); +// } + +// public void UseShardingTransaction(DbTransaction transaction) +// { +// throw new NotImplementedException(); +// } + +// public override EntityEntry Add(object entity) +// { +// return CreateGenericDbContext(entity).Add(entity); +// } + +// public override EntityEntry Add(TEntity entity) +// { +// return CreateGenericDbContext(entity).Add(entity); +// } + + +// public override ValueTask> AddAsync(TEntity entity, CancellationToken cancellationToken = new CancellationToken()) +// { +// return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); +// } + +// public override ValueTask AddAsync(object entity, CancellationToken cancellationToken = new CancellationToken()) +// { +// return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); +// } + +// public override void AddRange(params object[] entities) +// { +// var groups = entities.Select(o => +// { +// var dbContext = CreateGenericDbContext(o); +// return new +// { +// DbContext = dbContext, +// Entity = o +// }; +// }).GroupBy(g => g.DbContext); + +// foreach (var group in groups) +// { +// group.Key.AddRange(group.Select(o => o.Entity)); +// } +// } + +// public override void AddRange(IEnumerable entities) +// { +// var groups = entities.Select(o => +// { +// var dbContext = CreateGenericDbContext(o); +// return new +// { +// DbContext = dbContext, +// Entity = o +// }; +// }).GroupBy(g => g.DbContext); + +// foreach (var group in groups) +// { +// group.Key.AddRange(group.Select(o => o.Entity)); +// } +// } + +// public override async Task AddRangeAsync(params object[] entities) +// { +// var groups = entities.Select(o => +// { +// var dbContext = CreateGenericDbContext(o); +// return new +// { +// DbContext = dbContext, +// Entity = o +// }; +// }).GroupBy(g => g.DbContext); + +// foreach (var group in groups) +// { +// await group.Key.AddRangeAsync(group.Select(o => o.Entity)); +// } +// } + +// public override async Task AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = new CancellationToken()) +// { +// var groups = entities.Select(o => +// { +// var dbContext = CreateGenericDbContext(o); +// return new +// { +// DbContext = dbContext, +// Entity = o +// }; +// }).GroupBy(g => g.DbContext); + +// foreach (var group in groups) +// { +// await group.Key.AddRangeAsync(group.Select(o => o.Entity)); +// } +// } + +// public override EntityEntry Attach(TEntity entity) +// { +// return CreateGenericDbContext(entity).Attach(entity); +// } + +// public override EntityEntry Attach(object entity) +// { +// return CreateGenericDbContext(entity).Attach(entity); +// } + +// public override void AttachRange(params object[] entities) +// { +// var groups = entities.Select(o => +// { +// var dbContext = CreateGenericDbContext(o); +// return new +// { +// DbContext = dbContext, +// Entity = o +// }; +// }).GroupBy(g => g.DbContext); + +// foreach (var group in groups) +// { +// group.Key.AttachRange(group.Select(o => o.Entity)); +// } +// } + +// public override void AttachRange(IEnumerable entities) +// { +// var groups = entities.Select(o => +// { +// var dbContext = CreateGenericDbContext(o); +// return new +// { +// DbContext = dbContext, +// Entity = o +// }; +// }).GroupBy(g => g.DbContext); + +// foreach (var group in groups) +// { +// group.Key.AttachRange(group.Select(o => o.Entity)); +// } +// } + + +// //public override DatabaseFacade Database => _dbContextCaches.Any() +// // ? _dbContextCaches.First().Value.Database +// // : GetDbContext(true, string.Empty).Database; + +// public override EntityEntry Entry(TEntity entity) +// { +// return CreateGenericDbContext(entity).Entry(entity); +// } + +// public override EntityEntry Entry(object entity) +// { +// return CreateGenericDbContext(entity).Entry(entity); +// } + +// public override EntityEntry Update(TEntity entity) +// { +// return CreateGenericDbContext(entity).Update(entity); +// } + +// public override EntityEntry Update(object entity) +// { +// return CreateGenericDbContext(entity).Update(entity); +// } + +// public override void UpdateRange(params object[] entities) +// { +// var groups = entities.Select(o => +// { +// var dbContext = CreateGenericDbContext(o); +// return new +// { +// DbContext = dbContext, +// Entity = o +// }; +// }).GroupBy(g => g.DbContext); + +// foreach (var group in groups) +// { +// group.Key.UpdateRange(group.Select(o => o.Entity)); +// } +// } + +// public override void UpdateRange(IEnumerable entities) +// { +// var groups = entities.Select(o => +// { +// var dbContext = CreateGenericDbContext(o); +// return new +// { +// DbContext = dbContext, +// Entity = o +// }; +// }).GroupBy(g => g.DbContext); + +// foreach (var group in groups) +// { +// group.Key.UpdateRange(group.Select(o => o.Entity)); +// } +// } + +// public override EntityEntry Remove(TEntity entity) +// { +// return CreateGenericDbContext(entity).Remove(entity); +// } + +// public override EntityEntry Remove(object entity) +// { +// return CreateGenericDbContext(entity).Remove(entity); +// } + +// public override void RemoveRange(params object[] entities) +// { +// var groups = entities.Select(o => +// { +// var dbContext = CreateGenericDbContext(o); +// return new +// { +// DbContext = dbContext, +// Entity = o +// }; +// }).GroupBy(g => g.DbContext); + +// foreach (var group in groups) +// { +// group.Key.RemoveRange(group.Select(o => o.Entity)); +// } +// } + +// public override void RemoveRange(IEnumerable entities) +// { +// var groups = entities.Select(o => +// { +// var dbContext = CreateGenericDbContext(o); +// return new +// { +// DbContext = dbContext, +// Entity = o +// }; +// }).GroupBy(g => g.DbContext); + +// foreach (var group in groups) +// { +// group.Key.RemoveRange(group.Select(o => o.Entity)); +// } +// } + +// public override int SaveChanges() +// { +// var isBeginTransaction = IsBeginTransaction; +// //如果是内部开的事务就内部自己消化 +// if (!isBeginTransaction) +// { +// Database.BeginTransaction(); +// } + +// int i = 0; + +// try +// { +// foreach (var dbContextCache in _dbContextCaches) +// { +// dbContextCache.Value.Database.UseTransaction(Database.CurrentTransaction.GetDbTransaction()); +// i += dbContextCache.Value.SaveChanges(); +// } + +// if (!isBeginTransaction) +// Database.CurrentTransaction.Commit(); +// } +// finally +// { +// if (!isBeginTransaction) +// { +// Database.CurrentTransaction?.Dispose(); +// foreach (var dbContextCache in _dbContextCaches) +// { +// dbContextCache.Value.Database.UseTransaction(null); +// } +// } +// } + +// return i; +// } + +// public override int SaveChanges(bool acceptAllChangesOnSuccess) +// { +// var isBeginTransaction = IsBeginTransaction; +// //如果是内部开的事务就内部自己消化 +// if (!isBeginTransaction) +// { +// Database.BeginTransaction(); +// } + +// int i = 0; + +// try +// { +// foreach (var dbContextCache in _dbContextCaches) +// { +// dbContextCache.Value.Database.UseTransaction(Database.CurrentTransaction.GetDbTransaction()); +// i += dbContextCache.Value.SaveChanges(acceptAllChangesOnSuccess); +// } + +// if (!isBeginTransaction) +// Database.CurrentTransaction.Commit(); +// } +// finally +// { +// if (!isBeginTransaction) +// { +// Database.CurrentTransaction?.Dispose(); +// foreach (var dbContextCache in _dbContextCaches) +// { +// dbContextCache.Value.Database.UseTransaction(null); +// } +// } +// } + +// return i; +// } + + +// public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) +// { +// var isBeginTransaction = IsBeginTransaction; +// //如果是内部开的事务就内部自己消化 +// if (!isBeginTransaction) +// { +// await Database.BeginTransactionAsync(cancellationToken); +// } + +// int i = 0; + +// try +// { +// foreach (var dbContextCache in _dbContextCaches) +// { +// await dbContextCache.Value.Database.UseTransactionAsync(Database.CurrentTransaction.GetDbTransaction(), cancellationToken: cancellationToken); +// i += await dbContextCache.Value.SaveChangesAsync(cancellationToken); +// } + +// if (!isBeginTransaction) +// await Database.CurrentTransaction.CommitAsync(cancellationToken); +// } +// finally +// { +// if (!isBeginTransaction) +// { +// } + +// if (Database.CurrentTransaction != null) +// { +// await Database.CurrentTransaction.DisposeAsync(); +// foreach (var dbContextCache in _dbContextCaches) +// { +// await dbContextCache.Value.Database.UseTransactionAsync(null, cancellationToken: cancellationToken); +// } +// } +// } + +// return i; +// } + +// public override async Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken()) +// { +// var isBeginTransaction = IsBeginTransaction; +// //如果是内部开的事务就内部自己消化 +// if (!isBeginTransaction) +// { +// await Database.BeginTransactionAsync(cancellationToken); +// } + +// int i = 0; + +// try +// { +// foreach (var dbContextCache in _dbContextCaches) +// { +// await dbContextCache.Value.Database.UseTransactionAsync(Database.CurrentTransaction.GetDbTransaction(), cancellationToken: cancellationToken); +// i += await dbContextCache.Value.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); +// } + +// if (!isBeginTransaction) +// await Database.CurrentTransaction.CommitAsync(cancellationToken); +// } +// finally +// { +// if (!isBeginTransaction) +// if (Database.CurrentTransaction != null) +// { +// await Database.CurrentTransaction.DisposeAsync(); + +// foreach (var dbContextCache in _dbContextCaches) +// { +// await dbContextCache.Value.Database.UseTransactionAsync(null, cancellationToken: cancellationToken); +// } +// } +// } + +// return i; +// } + +// public override void Dispose() +// { +// foreach (var dbContextCache in _dbContextCaches) +// { +// try +// { +// dbContextCache.Value.Dispose(); +// } +// catch (Exception e) +// { +// Console.WriteLine(e); +// } +// } + +// base.Dispose(); +// } + +// public override async ValueTask DisposeAsync() +// { +// foreach (var dbContextCache in _dbContextCaches) +// { +// try +// { +// await dbContextCache.Value.DisposeAsync(); +// } +// catch (Exception e) +// { +// Console.WriteLine(e); +// } +// } + +// await base.DisposeAsync(); +// } +// } +//} \ No newline at end of file diff --git a/samples/Samples.AutoByDate.SqlServer/DbContexts/DefaultShardingDbContext.cs b/samples/Samples.AutoByDate.SqlServer/DbContexts/DefaultShardingDbContext.cs index f34e8907..46595404 100644 --- a/samples/Samples.AutoByDate.SqlServer/DbContexts/DefaultShardingDbContext.cs +++ b/samples/Samples.AutoByDate.SqlServer/DbContexts/DefaultShardingDbContext.cs @@ -18,6 +18,5 @@ namespace Samples.AutoByDate.SqlServer.DbContexts modelBuilder.ApplyConfiguration(new TestLogByWeekMap()); } - public override Type ShardingDbContextType => this.GetType(); } } diff --git a/samples/Samples.AutoByDate.SqlServer/Jobs/AutoCreateTableByDay.cs b/samples/Samples.AutoByDate.SqlServer/Jobs/AutoCreateTableByDay.cs index 7e5fe6fa..951bfb7a 100644 --- a/samples/Samples.AutoByDate.SqlServer/Jobs/AutoCreateTableByDay.cs +++ b/samples/Samples.AutoByDate.SqlServer/Jobs/AutoCreateTableByDay.cs @@ -4,6 +4,7 @@ using ChronusJob.Jobs.Attributes; using Samples.AutoByDate.SqlServer.DbContexts; using Samples.AutoByDate.SqlServer.Domain.Entities; using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase.VirtualTables; using ShardingCore.Core.VirtualTables; using ShardingCore.TableCreator; @@ -17,31 +18,31 @@ namespace Samples.AutoByDate.SqlServer.Jobs */ public class AutoCreateTableByDay : IJob { - ///// - ///// 每天中午12点执行,启动的时候执行以下 - ///// - ///// - ///// - //[JobRun(Name = "定时创建分表组件", Cron = "0 0 12 * * ?", RunOnceOnStart = true)] + /// + /// 每天中午12点执行,启动的时候执行以下 + /// + /// + /// + [JobRun(Name = "定时创建分表组件", Cron = "0 0 12 * * ?", RunOnceOnStart = true)] - //public void AutoCreateTable(IVirtualTableManager virtualTableManager, IShardingTableCreator tableCreator) - //{ - // var virtualTable = virtualTableManager.GetVirtualTable(); - // if (virtualTable == null) - // { - // return; - // } - // var now = DateTime.Now.Date.AddDays(1); - // var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); - // try - // { - // virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, tail)); - // tableCreator.CreateTable(tail); - // } - // catch (Exception e) - // { - // //ignore - // } - //} + public void AutoCreateTable(IVirtualTableManager virtualTableManager, IShardingTableCreator tableCreator) + { + var virtualTable = virtualTableManager.GetVirtualTable(typeof(SysUserLogByDay)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(1); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + try + { + virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, tail)); + tableCreator.CreateTable("ds0",typeof(SysUserLogByDay),tail); + } + catch (Exception e) + { + //ignore + } + } } } \ No newline at end of file diff --git a/samples/Samples.AutoByDate.SqlServer/Startup.cs b/samples/Samples.AutoByDate.SqlServer/Startup.cs index d828e313..cc9affff 100644 --- a/samples/Samples.AutoByDate.SqlServer/Startup.cs +++ b/samples/Samples.AutoByDate.SqlServer/Startup.cs @@ -35,16 +35,20 @@ namespace Samples.AutoByDate.SqlServer services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Samples.AutoByDate.SqlServer", Version = "v1"}); }); services.AddShardingDbContext( - o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDBxx2;Integrated Security=True;") - , op => + o => o.UseSqlServer( + "Data Source=localhost;Initial Catalog=ShardingCoreDBxx2;Integrated Security=True;") + ).Begin(true) + .AddShardingQuery((conStr, builder) => builder.UseSqlServer(conStr) + .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)) + .AddShardingTransaction((connection, builder) => + builder.UseSqlServer(connection)) + .AddDefaultDataSource("ds0", + "Data Source=localhost;Initial Catalog=ShardingCoreDB;Integrated Security=True;") + .AddShardingTable(o => { - op.EnsureCreatedWithOutShardingTable = true; - op.CreateShardingTableOnStart = true; - op.UseShardingOptionsBuilder((connection, builder) => builder.UseSqlServer(connection), - (conStr,builder) => builder.UseSqlServer(conStr)); - op.AddShardingTableRoute(); - op.AddShardingTableRoute(); - }); + o.AddShardingTableRoute(); + o.AddShardingTableRoute(); + }).End(); services.AddChronusJob(); } diff --git a/src/ShardingCore/Core/PhysicTables/DefaultPhysicTable.cs b/src/ShardingCore/Core/PhysicTables/DefaultPhysicTable.cs index 9670cdf9..1ba121e3 100644 --- a/src/ShardingCore/Core/PhysicTables/DefaultPhysicTable.cs +++ b/src/ShardingCore/Core/PhysicTables/DefaultPhysicTable.cs @@ -43,10 +43,27 @@ namespace ShardingCore.Core.PhysicTables return Equals((DefaultPhysicTable)obj); } +#if !EFCORE2 + public override int GetHashCode() { - return HashCode.Combine(OriginalName, Tail, EntityType); + return HashCode.Combine(OriginalName, Tail, VirtualTable); } +#endif + +#if EFCORE2 + + public override int GetHashCode() + { + unchecked + { + var hashCode = (OriginalName != null ? OriginalName.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (Tail != null ? Tail.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (VirtualTable != null ? VirtualTable.GetHashCode() : 0); + return hashCode; + } + } +#endif } } \ No newline at end of file diff --git a/src/ShardingCore/Core/VirtualDatabase/ShardingEntityConfig.cs b/src/ShardingCore/Core/VirtualDatabase/ShardingEntityConfig.cs index 36b13a37..40e9c051 100644 --- a/src/ShardingCore/Core/VirtualDatabase/ShardingEntityConfig.cs +++ b/src/ShardingCore/Core/VirtualDatabase/ShardingEntityConfig.cs @@ -43,7 +43,7 @@ namespace ShardingCore.Core.VirtualDatabase /// /// 分表的原表名 original table name in db exclude tail /// - public string OriginalTableName { get; set; } + public string VirtualTableName { get; set; } /// /// 启动时是否建表 auto create table when start app diff --git a/src/ShardingCore/Core/VirtualDatabase/VirtualTables/DefaultVirtualTable.cs b/src/ShardingCore/Core/VirtualDatabase/VirtualTables/DefaultVirtualTable.cs index 949ab6ec..8532af4e 100644 --- a/src/ShardingCore/Core/VirtualDatabase/VirtualTables/DefaultVirtualTable.cs +++ b/src/ShardingCore/Core/VirtualDatabase/VirtualTables/DefaultVirtualTable.cs @@ -9,6 +9,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using ShardingCore.Core.VirtualDatabase; using ShardingCore.Core.VirtualDatabase.VirtualDataSources; namespace ShardingCore.Core.VirtualTables @@ -34,7 +35,7 @@ namespace ShardingCore.Core.VirtualTables /// /// 分表的配置 /// - public ShardingTableConfig ShardingConfig { get; } + public ShardingEntityConfig ShardingConfig { get; } /// /// 分库配置 /// @@ -51,7 +52,7 @@ namespace ShardingCore.Core.VirtualTables { _virtualTableRoute = virtualTableRoute; EntityType = typeof(T); - ShardingConfig = ShardingKeyUtil.Parse(EntityType); + ShardingConfig = ShardingUtil.Parse(EntityType); var paginationConfiguration = virtualTableRoute.CreatePaginationConfiguration(); if (paginationConfiguration != null) { @@ -78,7 +79,7 @@ namespace ShardingCore.Core.VirtualTables shardingKeyValue = tableRouteConfig.GetShardingKeyValue(); if (tableRouteConfig.UseEntity()) - shardingKeyValue = tableRouteConfig.GetShardingEntity().GetPropertyValue(ShardingConfig.ShardingField); + shardingKeyValue = tableRouteConfig.GetShardingEntity().GetPropertyValue(ShardingConfig.ShardingTableField); if (shardingKeyValue == null) throw new ShardingCoreException(" route entity queryable or sharding key value is null "); @@ -94,14 +95,14 @@ namespace ShardingCore.Core.VirtualTables return _physicTables.TryAdd(physicTable, null); } - public void SetOriginalTableName(string originalTableName) + public void SetVirtualTableName(string originalTableName) { - ShardingConfig.ShardingOriginalTable = originalTableName; + ShardingConfig.VirtualTableName = originalTableName; } public string GetVirtualTableName() { - return ShardingConfig.ShardingOriginalTable; + return ShardingConfig.VirtualTableName; } IVirtualTableRoute IVirtualTable.GetVirtualRoute() diff --git a/src/ShardingCore/Core/VirtualDatabase/VirtualTables/IVirtualTable.cs b/src/ShardingCore/Core/VirtualDatabase/VirtualTables/IVirtualTable.cs index 42290648..790b59e5 100644 --- a/src/ShardingCore/Core/VirtualDatabase/VirtualTables/IVirtualTable.cs +++ b/src/ShardingCore/Core/VirtualDatabase/VirtualTables/IVirtualTable.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase; using ShardingCore.Core.VirtualDatabase.VirtualDataSources; using ShardingCore.Core.VirtualRoutes.TableRoutes; using ShardingCore.Sharding.PaginationConfigurations; @@ -25,7 +26,7 @@ namespace ShardingCore.Core.VirtualTables /// /// 分表配置 /// - ShardingTableConfig ShardingConfig { get; } + ShardingEntityConfig ShardingConfig { get; } /// /// 分页配置 /// @@ -60,7 +61,7 @@ namespace ShardingCore.Core.VirtualTables /// /// /// - void SetOriginalTableName(string originalTableName); + void SetVirtualTableName(string originalTableName); /// /// 获取原始表名 get original table name /// diff --git a/src/ShardingCore/Core/VirtualDatabase/VirtualTables/ShardingTableConfig.cs b/src/ShardingCore/Core/VirtualDatabase/VirtualTables/ShardingTableConfig.cs index 82f91a80..a9bb8496 100644 --- a/src/ShardingCore/Core/VirtualDatabase/VirtualTables/ShardingTableConfig.cs +++ b/src/ShardingCore/Core/VirtualDatabase/VirtualTables/ShardingTableConfig.cs @@ -1,41 +1,41 @@ -using System; +//using System; -namespace ShardingCore.Core.VirtualTables -{ -/* -* @Author: xjm -* @Description: -* @Date: Wednesday, 16 December 2020 13:24:05 -* @Email: 326308290@qq.com -*/ - /// - /// 分表配置 sharding config - /// - public class ShardingTableConfig - { - /// - /// 分表类型 sharding entity type - /// - public Type ShardingEntityType { get; set; } +//namespace ShardingCore.Core.VirtualTables +//{ +///* +//* @Author: xjm +//* @Description: +//* @Date: Wednesday, 16 December 2020 13:24:05 +//* @Email: 326308290@qq.com +//*/ +// /// +// /// 分表配置 sharding config +// /// +// public class ShardingTableConfig +// { +// /// +// /// 分表类型 sharding entity type +// /// +// public Type ShardingEntityType { get; set; } - /// - /// 分表字段 sharding field - /// - public string ShardingField { get; set; } +// /// +// /// 分表字段 sharding field +// /// +// public string ShardingField { get; set; } - /// - /// 分表的原表名 original table name in db exclude tail - /// - public string ShardingOriginalTable { get; set; } +// /// +// /// 分表的原表名 original table name in db exclude tail +// /// +// public string ShardingOriginalTable { get; set; } - /// - /// 启动时是否建表 auto create table when start app - /// - public bool? AutoCreateTable { get; set; } +// /// +// /// 启动时是否建表 auto create table when start app +// /// +// public bool? AutoCreateTable { get; set; } - /// - /// 分表尾巴后缀 table sharding tail prefix - /// - public string TailPrefix { get; set; } = "_"; - } -} \ No newline at end of file +// /// +// /// 分表尾巴后缀 table sharding tail prefix +// /// +// public string TailPrefix { get; set; } = "_"; +// } +//} \ No newline at end of file diff --git a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/IVirtualDataSourceRoute.cs b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/IVirtualDataSourceRoute.cs index 6586a08d..ac4f8db1 100644 --- a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/IVirtualDataSourceRoute.cs +++ b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/IVirtualDataSourceRoute.cs @@ -30,7 +30,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes /// data source name string RouteWithValue(object shardingKeyValue); - ISet GetAllDataSourceNames(); + List GetAllDataSourceNames(); } diff --git a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/RouteRuleEngine/DataSourceRouteRuleEngine.cs b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/RouteRuleEngine/DataSourceRouteRuleEngine.cs index 06d58a4d..ed78b194 100644 --- a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/RouteRuleEngine/DataSourceRouteRuleEngine.cs +++ b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/RouteRuleEngine/DataSourceRouteRuleEngine.cs @@ -30,7 +30,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.RouteRuleEngine { var dataSourceMaps = new Dictionary>(); var notShardingDataSourceEntityType = routeRuleContext.QueryEntities.FirstOrDefault(o => !o.IsShardingDataSource()); - //存在不分表的 + //存在不分库的 if (notShardingDataSourceEntityType != null) dataSourceMaps.Add(notShardingDataSourceEntityType, new HashSet() { _virtualDataSource.DefaultDataSourceName }); diff --git a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingOperatorVirtualTableRoute.cs b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingOperatorVirtualTableRoute.cs index 102e7f0c..6caef76b 100644 --- a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingOperatorVirtualTableRoute.cs +++ b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingOperatorVirtualTableRoute.cs @@ -22,7 +22,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions protected override List DoRouteWithPredicate(List allPhysicTables, IQueryable queryable) { //获取所有需要路由的表后缀 - var filter = ShardingKeyUtil.GetRouteShardingTableFilter(queryable, ShardingKeyUtil.Parse(typeof(T)), ConvertToShardingKey, GetRouteToFilter); + var filter = ShardingUtil.GetRouteShardingTableFilter(queryable, ShardingUtil.Parse(typeof(T)), ConvertToShardingKey, GetRouteToFilter); var physicTables = allPhysicTables.Where(o => filter(o.Tail)).ToList(); return physicTables; } @@ -43,8 +43,8 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions var physicTables = allPhysicTables.Where(o => o.Tail== shardingKeyToTail).ToList(); if (physicTables.IsEmpty()) { - var routeConfig = ShardingKeyUtil.Parse(typeof(T)); - throw new ShardingKeyRouteNotMatchException($"{routeConfig.ShardingEntityType} -> [{routeConfig.ShardingField}] ->【{shardingKey}】 all tails ->[{string.Join(",", allPhysicTables.Select(o=>o.FullName))}]"); + var routeConfig = ShardingUtil.Parse(typeof(T)); + throw new ShardingKeyRouteNotMatchException($"{routeConfig.EntityType} -> [{routeConfig.ShardingTableField}] ->【{shardingKey}】 all tails ->[{string.Join(",", allPhysicTables.Select(o=>o.FullName))}]"); } if (physicTables.Count > 1) diff --git a/src/ShardingCore/EFCores/ShardingDbSetSource.cs b/src/ShardingCore/EFCores/ShardingDbSetSource.cs index 831797d0..11cce6fe 100644 --- a/src/ShardingCore/EFCores/ShardingDbSetSource.cs +++ b/src/ShardingCore/EFCores/ShardingDbSetSource.cs @@ -12,9 +12,9 @@ namespace ShardingCore.EFCores * @Date: Saturday, 14 August 2021 10:17:43 * @Email: 326308290@qq.com */ - public class ShardingDbSetSource:IDbSetSource - { #if EFCORE5 + public class ShardingDbSetSource : IDbSetSource + { private static readonly MethodInfo _genericCreateSet = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); @@ -50,8 +50,11 @@ namespace ShardingCore.EFCores private static Func CreateSetFactory() where TEntity : class => (c, name) => new ShardingInternalDbSet(c, name); + } #endif #if EFCORE3 + public class ShardingDbSetSource:IDbSetSource + { private static readonly MethodInfo _genericCreateSet = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); @@ -78,7 +81,55 @@ namespace ShardingCore.EFCores private static Func CreateSetFactory() where TEntity : class => c => new ShardingInternalDbSet(c); +} #endif + +#if EFCORE2 + + /// + /// 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/ShardingInternalDbQuery.cs new file mode 100644 index 00000000..c6481315 --- /dev/null +++ b/src/ShardingCore/EFCores/ShardingInternalDbQuery.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; + +namespace ShardingCore.EFCores +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/8/20 17:05:36 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ +#if EFCORE2 + + public class ShardingInternalDbQuery : InternalDbQuery where TQuery : class + { + public ShardingInternalDbQuery(DbContext context) : base(context) + { + } + } +#endif +} diff --git a/src/ShardingCore/EFCores/ShardingInternalDbSet.cs b/src/ShardingCore/EFCores/ShardingInternalDbSet.cs index 750549f0..6f191d41 100644 --- a/src/ShardingCore/EFCores/ShardingInternalDbSet.cs +++ b/src/ShardingCore/EFCores/ShardingInternalDbSet.cs @@ -55,6 +55,7 @@ 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 !EFCORE2 public override async ValueTask> AddAsync( TEntity entity, CancellationToken cancellationToken = default) @@ -63,6 +64,15 @@ namespace ShardingCore.EFCores return await genericDbContext.AddAsync(entity, cancellationToken); } +#endif +#if EFCORE2 + public override async Task> AddAsync(TEntity entity, CancellationToken cancellationToken = new CancellationToken()) + { + var genericDbContext = ((IShardingDbContext)_context).CreateGenericDbContext(entity); + return await genericDbContext.AddAsync(entity, cancellationToken); + } + +#endif /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/ShardingCore/EFCores/ShardingModelCustomizer.cs b/src/ShardingCore/EFCores/ShardingModelCustomizer.cs index 05a95ebf..8544e78d 100644 --- a/src/ShardingCore/EFCores/ShardingModelCustomizer.cs +++ b/src/ShardingCore/EFCores/ShardingModelCustomizer.cs @@ -69,11 +69,11 @@ namespace ShardingCore.EFCores private void MappingToTable(Type clrType, ModelBuilder modelBuilder, string tail) { - var shardingEntityConfig = ShardingKeyUtil.Parse(clrType); - var shardingEntity = shardingEntityConfig.ShardingEntityType; + var shardingEntityConfig = ShardingUtil.Parse(clrType); + var shardingEntity = shardingEntityConfig.EntityType; var tailPrefix = shardingEntityConfig.TailPrefix; var entity = modelBuilder.Entity(shardingEntity); - var tableName = shardingEntityConfig.ShardingOriginalTable; + var tableName = shardingEntityConfig.VirtualTableName; if (string.IsNullOrWhiteSpace(tableName)) throw new ArgumentNullException($"{shardingEntity}: not found original table name。"); #if DEBUG diff --git a/src/ShardingCore/EFCores/ShardingQueryCompiler.cs b/src/ShardingCore/EFCores/ShardingQueryCompiler.cs index be9d62d1..b18fd17a 100644 --- a/src/ShardingCore/EFCores/ShardingQueryCompiler.cs +++ b/src/ShardingCore/EFCores/ShardingQueryCompiler.cs @@ -42,6 +42,8 @@ namespace ShardingCore.EFCores } +#if !EFCORE2 + public TResult ExecuteAsync(Expression query, CancellationToken cancellationToken) { return _shardingQueryExecutor.ExecuteAsync(_currentContext, query, cancellationToken); @@ -62,6 +64,36 @@ namespace ShardingCore.EFCores throw new NotImplementedException(); } +#endif + +#if EFCORE2 + + + public IAsyncEnumerable ExecuteAsync(Expression query) + { + return _shardingQueryExecutor.ExecuteAsync>(_currentContext, query); + } + + public Task ExecuteAsync(Expression query, CancellationToken cancellationToken) + { + return _shardingQueryExecutor.ExecuteAsync>(_currentContext, query, cancellationToken); + } + + public Func CreateCompiledQuery(Expression query) + { + throw new NotImplementedException(); + } + + public Func> CreateCompiledAsyncEnumerableQuery(Expression query) + { + throw new NotImplementedException(); + } + + public Func> CreateCompiledAsyncTaskQuery(Expression query) + { + throw new NotImplementedException(); + } +#endif } } \ No newline at end of file diff --git a/src/ShardingCore/Exceptions/ShardingDataSourceNotFoundException.cs b/src/ShardingCore/Exceptions/ShardingDataSourceNotFoundException.cs new file mode 100644 index 00000000..a96592ea --- /dev/null +++ b/src/ShardingCore/Exceptions/ShardingDataSourceNotFoundException.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.Serialization; + +namespace ShardingCore.Exceptions +{ +/* +* @Author: xjm +* @Description: +* @Date: Monday, 21 December 2020 09:32:54 +* @Email: 326308290@qq.com +*/ + public class ShardingDataSourceNotFoundException:ShardingCoreException + { + public ShardingDataSourceNotFoundException() + { + } + + protected ShardingDataSourceNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public ShardingDataSourceNotFoundException(string message) : base(message) + { + } + + public ShardingDataSourceNotFoundException(string message, Exception innerException) : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/src/ShardingCore/Exceptions/ShardingOwnerNotFoundException.cs b/src/ShardingCore/Exceptions/ShardingOwnerNotFoundException.cs deleted file mode 100644 index 85e87874..00000000 --- a/src/ShardingCore/Exceptions/ShardingOwnerNotFoundException.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ShardingCore.Exceptions -{ -/* -* @Author: xjm -* @Description: -* @Date: Monday, 21 December 2020 09:32:54 -* @Email: 326308290@qq.com -*/ - public class ShardingOwnerNotFoundException:Exception - { - public ShardingOwnerNotFoundException() - { - } - - protected ShardingOwnerNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - - public ShardingOwnerNotFoundException(string message) : base(message) - { - } - - public ShardingOwnerNotFoundException(string message, Exception innerException) : base(message, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/ShardingCore/Extensions/CommonExtension.cs b/src/ShardingCore/Extensions/CommonExtension.cs index cdcf3b2a..1a4691e9 100644 --- a/src/ShardingCore/Extensions/CommonExtension.cs +++ b/src/ShardingCore/Extensions/CommonExtension.cs @@ -93,7 +93,7 @@ namespace ShardingCore.Extensions public static ISet ParseQueryableRoute(this IQueryable queryable) { - return ShardingKeyUtil.GetQueryEntitiesFilter(queryable); + return ShardingUtil.GetQueryEntitiesFilter(queryable); } public static T IfDo(this T t, bool @if,Func build) diff --git a/src/ShardingCore/Extensions/DbContextExtension.cs b/src/ShardingCore/Extensions/DbContextExtension.cs index f2aad489..ca488311 100644 --- a/src/ShardingCore/Extensions/DbContextExtension.cs +++ b/src/ShardingCore/Extensions/DbContextExtension.cs @@ -133,6 +133,9 @@ var contextModelRelationalModel = contextModel.RelationalModel as RelationalMode var syncObject = modelSourceImpl.GetFieldValue("_syncObject"); return syncObject; #endif +#if EFCORE2 + return sLock; +#endif } diff --git a/src/ShardingCore/Extensions/ShardingExtension.cs b/src/ShardingCore/Extensions/ShardingExtension.cs index afa24757..33cfe0b9 100644 --- a/src/ShardingCore/Extensions/ShardingExtension.cs +++ b/src/ShardingCore/Extensions/ShardingExtension.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -60,7 +61,12 @@ namespace ShardingCore.Extensions public static string ShardingPrint(this Expression expression) { +#if !EFCORE2 return expression.Print(); +#endif +#if EFCORE2 + return expression.ToString(); +#endif } public static string ShardingPrint(this IQueryable queryable) { @@ -75,110 +81,125 @@ namespace ShardingCore.Extensions /// /// /// - public static IDictionary> BulkShardingEnumerable(this TShardingDbContext shardingDbContext, - IEnumerable entities) where TShardingDbContext:DbContext,IShardingDbContext where TEntity : class + public static Dictionary>> BulkShardingEnumerable(this TShardingDbContext shardingDbContext, + IEnumerable entities) where TShardingDbContext : DbContext, IShardingDbContext where TEntity : class { var entityType = typeof(TEntity); var routeTailFactory = ShardingContainer.GetService(); var virtualDataSource = ShardingContainer.GetService>(); - var dataSourceNames = new HashSet(); + var dataSourceNames = new Dictionary>>(); + var entitiesArray = entities as TEntity[] ?? entities.ToArray(); if (!entityType.IsShardingDataSource()) { - dataSourceNames.Add(virtualDataSource.DefaultDataSourceName); + var bulkDicEntries = new Dictionary>(); + dataSourceNames.Add(virtualDataSource.DefaultDataSourceName, bulkDicEntries); + + var isShardingTable = entityType.IsShardingTable(); + IVirtualTable virtualTable = null; + IVirtualTableRoute virtualTableRoute = null; + ISet allTails = null; + if (isShardingTable) + { + var virtualTableManager = ShardingContainer.GetService>(); + virtualTable = virtualTableManager.GetVirtualTable(entityType); + virtualTableRoute = virtualTable.GetVirtualRoute(); + allTails = virtualTableRoute.GetAllTails().ToHashSet(); + } + foreach (var entity in entitiesArray) + { + if (isShardingTable) + BulkShardingTableEnumerable(shardingDbContext, virtualDataSource.DefaultDataSourceName, bulkDicEntries, + routeTailFactory, virtualTable, virtualTableRoute, allTails, entity); + else + BulkNoShardingTableEnumerable(shardingDbContext, virtualDataSource.DefaultDataSourceName, bulkDicEntries, + routeTailFactory, entity); + } } else { var virtualDataSourceRoute = virtualDataSource.GetRoute(entityType); - var allDataSourceNames = virtualDataSourceRoute.GetAllDataSourceNames(); + var allDataSourceNames = virtualDataSourceRoute.GetAllDataSourceNames().ToHashSet(); var shardingEntityConfig = ShardingUtil.Parse(entityType); - foreach (var entity in entities) + var isShardingTable = entityType.IsShardingTable(); + IVirtualTable virtualTable = null; + IVirtualTableRoute virtualTableRoute = null; + ISet allTails = null; + if (isShardingTable) { - entity.GetPropertyValue(shardingEntityConfig.ShardingDataSourceField) - virtualDataSourceRoute.ShardingKeyToDataSourceName(entity) - dataSourceNames.Add(dataSourceName); + var virtualTableManager = ShardingContainer.GetService>(); + virtualTable = virtualTableManager.GetVirtualTable(entityType); + virtualTableRoute = virtualTable.GetVirtualRoute(); + allTails = virtualTableRoute.GetAllTails().ToHashSet(); } - } - if (!entityType.IsShardingDataSource()) - { - virtualDataSource. - var defaultDataSourceName = virtualDataSource.DefaultDataSourceName; - if (!entityType.IsShardingTable()) + foreach (var entity in entitiesArray) { - var routeTail = routeTailFactory.Create(string.Empty); - var dbContext = shardingDbContext.GetDbContext(defaultDataSourceName, true, routeTail); - return new Dictionary>() + var shardingDataSourceValue = entity.GetPropertyValue(shardingEntityConfig.ShardingDataSourceField); + if (shardingDataSourceValue == null) + throw new InvalidOperationException($" etities has null value of sharding data source value"); + var shardingDataSourceName = virtualDataSourceRoute.ShardingKeyToDataSourceName(shardingDataSourceValue); + if (!allDataSourceNames.Contains(shardingDataSourceName)) + throw new ShardingDataSourceNotFoundException( + $" data source name :[{shardingDataSourceName}] all data source names:[{string.Join(",", allDataSourceNames)}]"); + if (!dataSourceNames.TryGetValue(shardingDataSourceName, out var bulkDicEntries)) { - {dbContext,entities } - }; - } - else - { - var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(shardingDbContext.ShardingDbContextType)); - var virtualTable = virtualTableManager.GetVirtualTable(entityType); - - var virtualTableRoute = virtualTable.GetVirtualRoute(); - var hashSet = virtualTableRoute.GetAllTails().ToHashSet(); - var dic = new Dictionary>(); - foreach (var entity in entities) - { - var shardingKey = entity.GetPropertyValue(virtualTable.ShardingConfig.ShardingField); - var tail = virtualTableRoute.ShardingKeyToTail(shardingKey); - if (!hashSet.Contains(tail)) - throw new ShardingKeyRouteNotMatchException( - $"Entity:{entityType.FullName},ShardingKey:{shardingKey},ShardingTail:{tail}"); - - var routeTail = routeTailFactory.Create(tail); - var routeTailIdentity = routeTail.GetRouteTailIdentity(); - if (!dic.TryGetValue(routeTailIdentity, out var bulkDicEntry)) - { - var dbContext = shardingDbContext.GetDbContext(defaultDataSourceName, true, routeTail); - bulkDicEntry = new BulkDicEntry(dbContext, new LinkedList()); - dic.Add(routeTailIdentity, bulkDicEntry); - } - - bulkDicEntry.InnerEntities.AddLast(entity); + bulkDicEntries = new Dictionary>(); + dataSourceNames.Add(shardingDataSourceName, bulkDicEntries); } - return dic.Values.ToDictionary(o => o.InnerDbContext, o => o.InnerEntities.Select(t => t)); - - } - } - else - { - if (!entityType.IsShardingTable()) - { - - } - - var virtualTableManager = ShardingContainer.GetService(); - var virtualTable = virtualTableManager.GetVirtualTable(shardingDbContext.ShardingDbContextType, entityType); - - var virtualTableRoute = virtualTable.GetVirtualRoute(); - var hashSet = virtualTableRoute.GetAllTails().ToHashSet(); - var dic = new Dictionary>(); - foreach (var entity in entities) - { - var shardingKey = entity.GetPropertyValue(virtualTable.ShardingConfig.ShardingField); - var tail = virtualTableRoute.ShardingKeyToTail(shardingKey); - if (!hashSet.Contains(tail)) - throw new ShardingKeyRouteNotMatchException( - $"Entity:{entityType.FullName},ShardingKey:{shardingKey},ShardingTail:{tail}"); - - var routeTail = routeTailFactory.Create(tail); - var routeTailIdentity = routeTail.GetRouteTailIdentity(); - if (!dic.TryGetValue(routeTailIdentity, out var bulkDicEntry)) + if (isShardingTable) { - var dbContext = shardingDbContext.GetDbContext(true, routeTail); - bulkDicEntry = new BulkDicEntry(dbContext, new LinkedList()); - dic.Add(routeTailIdentity, bulkDicEntry); + BulkShardingTableEnumerable(shardingDbContext, shardingDataSourceName, bulkDicEntries, + routeTailFactory, virtualTable, virtualTableRoute, allTails, entity); } - - bulkDicEntry.InnerEntities.AddLast(entity); + else + BulkNoShardingTableEnumerable(shardingDbContext, shardingDataSourceName, bulkDicEntries, + routeTailFactory, entity); } - - return dic.Values.ToDictionary(o => o.InnerDbContext, o => o.InnerEntities.Select(t => t)); } + + return dataSourceNames.ToDictionary(o => o.Key, + o => o.Value.Values.ToDictionary(v => v.InnerDbContext, v => v.InnerEntities.Select(t => t))); + } + + private static void BulkShardingTableEnumerable(TShardingDbContext shardingDbContext, string dataSourceName, Dictionary> dataSourceBulkDicEntries, + IRouteTailFactory routeTailFactory, IVirtualTable virtualTable,IVirtualTableRoute virtualTableRoute,ISet allTails, TEntity entity) + where TShardingDbContext : DbContext, IShardingDbContext + where TEntity : class + { + var entityType = typeof(TEntity); + + var shardingKey = entity.GetPropertyValue(virtualTable.ShardingConfig.ShardingTableField); + var tail = virtualTableRoute.ShardingKeyToTail(shardingKey); + if (!allTails.Contains(tail)) + throw new ShardingKeyRouteNotMatchException( + $"entity:{entityType.FullName},sharding key:{shardingKey},sharding tail:{tail}"); + + var routeTail = routeTailFactory.Create(tail); + var routeTailIdentity = routeTail.GetRouteTailIdentity(); + if (!dataSourceBulkDicEntries.TryGetValue(routeTailIdentity, out var bulkDicEntry)) + { + var dbContext = shardingDbContext.GetDbContext(dataSourceName, true, routeTail); + bulkDicEntry = new BulkDicEntry(dbContext, new LinkedList()); + dataSourceBulkDicEntries.Add(routeTailIdentity, bulkDicEntry); + } + + bulkDicEntry.InnerEntities.AddLast(entity); + } + private static void BulkNoShardingTableEnumerable(TShardingDbContext shardingDbContext, string dataSourceName, Dictionary> dataSourceBulkDicEntries, IRouteTailFactory routeTailFactory, TEntity entity) + where TShardingDbContext : DbContext, IShardingDbContext + where TEntity : class + { + var routeTail = routeTailFactory.Create(string.Empty); + var routeTailIdentity = routeTail.GetRouteTailIdentity(); + if (!dataSourceBulkDicEntries.TryGetValue(routeTailIdentity, out var bulkDicEntry)) + { + var dbContext = shardingDbContext.GetDbContext(dataSourceName, true, routeTail); + bulkDicEntry = new BulkDicEntry(dbContext, new LinkedList()); + dataSourceBulkDicEntries.Add(routeTailIdentity, bulkDicEntry); + } + + bulkDicEntry.InnerEntities.AddLast(entity); } internal class BulkDicEntry { diff --git a/src/ShardingCore/Sharding/AbstractShardingDbContext.cs b/src/ShardingCore/Sharding/AbstractShardingDbContext.cs index 4d8e5eeb..5308be44 100644 --- a/src/ShardingCore/Sharding/AbstractShardingDbContext.cs +++ b/src/ShardingCore/Sharding/AbstractShardingDbContext.cs @@ -104,6 +104,9 @@ namespace ShardingCore.Sharding } + +#if !EFCORE2 + public override ValueTask> AddAsync(TEntity entity, CancellationToken cancellationToken = new CancellationToken()) { return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); @@ -113,6 +116,18 @@ namespace ShardingCore.Sharding { return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); } +#endif +#if EFCORE2 + public override Task> AddAsync(TEntity entity, CancellationToken cancellationToken = new CancellationToken()) + { + return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); + } + + public override Task AddAsync(object entity, CancellationToken cancellationToken = new CancellationToken()) + { + return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); + } +#endif public override void AddRange(params object[] entities) { @@ -410,7 +425,12 @@ namespace ShardingCore.Sharding using(var tran= _shardingDbContextExecutor.BeginTransaction()) { i = await _shardingDbContextExecutor.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); - await tran.CommitAsync(cancellationToken); +#if EFCORE2 + tran.Commit(); +#endif +#if !EFCORE2 + await tran.CommitAsync(cancellationToken); +#endif } } else @@ -427,6 +447,7 @@ namespace ShardingCore.Sharding _shardingDbContextExecutor.Dispose(); base.Dispose(); } +#if !EFCORE2 public override async ValueTask DisposeAsync() { @@ -434,6 +455,7 @@ namespace ShardingCore.Sharding await base.DisposeAsync(); } +#endif public IShardingTransaction BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified) { diff --git a/src/ShardingCore/Sharding/Abstractions/IShardingDbContextExecutor.cs b/src/ShardingCore/Sharding/Abstractions/IShardingDbContextExecutor.cs index e819d455..ed97d667 100644 --- a/src/ShardingCore/Sharding/Abstractions/IShardingDbContextExecutor.cs +++ b/src/ShardingCore/Sharding/Abstractions/IShardingDbContextExecutor.cs @@ -21,7 +21,11 @@ namespace ShardingCore.Sharding.Abstractions * @Ver: 1.0 * @Email: 326308290@qq.com */ - public interface IShardingDbContextExecutor:IDisposable,IAsyncDisposable + public interface IShardingDbContextExecutor : IDisposable +#if !EFCORE2 + , IAsyncDisposable + +#endif { IShardingTransaction CurrentShardingTransaction { get; } bool IsBeginTransaction { get; } @@ -46,7 +50,7 @@ namespace ShardingCore.Sharding.Abstractions IEnumerable CreateExpressionDbContext(Expression> where) where TEntity : class; - IShardingTransaction BeginTransaction(IsolationLevel isolationLevel=IsolationLevel.Unspecified); + IShardingTransaction BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified); Task SaveChangesAsync(bool acceptAllChangesOnSuccess, diff --git a/src/ShardingCore/Sharding/Abstractions/IShardingQueryExecutor.cs b/src/ShardingCore/Sharding/Abstractions/IShardingQueryExecutor.cs index 47d0e080..a93fb670 100644 --- a/src/ShardingCore/Sharding/Abstractions/IShardingQueryExecutor.cs +++ b/src/ShardingCore/Sharding/Abstractions/IShardingQueryExecutor.cs @@ -3,6 +3,10 @@ using System.Threading; using Microsoft.EntityFrameworkCore.Infrastructure; using ShardingCore.Sharding.Enumerators; +#if EFCORE2 +using Microsoft.EntityFrameworkCore.Internal; +#endif + namespace ShardingCore.Sharding.Abstractions { /* diff --git a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/InMemoryReverseStreamMergeAsyncEnumerator.cs b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/InMemoryReverseStreamMergeAsyncEnumerator.cs index 1b6d50e3..0acfc8a6 100644 --- a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/InMemoryReverseStreamMergeAsyncEnumerator.cs +++ b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/InMemoryReverseStreamMergeAsyncEnumerator.cs @@ -24,6 +24,8 @@ namespace ShardingCore.Sharding.Enumerators.StreamMergeAsync { _inMemoryStreamMergeAsyncEnumerator = inMemoryStreamMergeAsyncEnumerator; } + +#if !EFCORE2 public async ValueTask DisposeAsync() { await _inMemoryStreamMergeAsyncEnumerator.DisposeAsync(); @@ -46,13 +48,32 @@ namespace ShardingCore.Sharding.Enumerators.StreamMergeAsync return _reverseEnumerator.MoveNext(); } +#endif +#if EFCORE2 + public async Task MoveNext(CancellationToken cancellationToken) + { + if (_first) + { + LinkedList _reverseCollection = new LinkedList(); + while (await _inMemoryStreamMergeAsyncEnumerator.MoveNext(cancellationToken)) + { + _reverseCollection.AddFirst(_inMemoryStreamMergeAsyncEnumerator.GetCurrent()); + } + + _reverseEnumerator = _reverseCollection.GetEnumerator(); + _first = false; + } + + return _reverseEnumerator.MoveNext(); + } +#endif public bool MoveNext() { if (_first) { LinkedList _reverseCollection = new LinkedList(); - while ( _inMemoryStreamMergeAsyncEnumerator.MoveNext()) + while (_inMemoryStreamMergeAsyncEnumerator.MoveNext()) { _reverseCollection.AddFirst(_inMemoryStreamMergeAsyncEnumerator.GetCurrent()); } @@ -91,7 +112,7 @@ namespace ShardingCore.Sharding.Enumerators.StreamMergeAsync public void Dispose() { - _inMemoryStreamMergeAsyncEnumerator.Dispose(); + _inMemoryStreamMergeAsyncEnumerator.Dispose(); _reverseEnumerator.Dispose(); } } diff --git a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/MultiAggregateOrderStreamMergeAsyncEnumerator.cs b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/MultiAggregateOrderStreamMergeAsyncEnumerator.cs index a1263969..75d7daf7 100644 --- a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/MultiAggregateOrderStreamMergeAsyncEnumerator.cs +++ b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/MultiAggregateOrderStreamMergeAsyncEnumerator.cs @@ -56,11 +56,21 @@ namespace ShardingCore.Sharding.Enumerators return _mergeContext.SelectContext.SelectProperties.Where(o => !o.IsAggregateMethod) .Select(o => first.GetValueByExpression(o.PropertyName)).ToList(); } +#if !EFCORE2 public async ValueTask MoveNextAsync() +#endif +#if EFCORE2 + public async Task MoveNext(CancellationToken cancellationToken = new CancellationToken()) +#endif { if (_queue.IsEmpty()) return false; +#if !EFCORE2 var hasNext = await SetCurrentValueAsync(); +#endif +#if EFCORE2 + var hasNext = await SetCurrentValueAsync(cancellationToken); +#endif if (hasNext) { CurrentGroupValues = _queue.IsEmpty() ? new List(0) : GetCurrentGroupValues(_queue.Peek()); @@ -79,7 +89,12 @@ namespace ShardingCore.Sharding.Enumerators return true; } +#if !EFCORE2 private async ValueTask SetCurrentValueAsync() +#endif +#if EFCORE2 + private async Task SetCurrentValueAsync(CancellationToken cancellationToken = new CancellationToken()) +#endif { CurrentValue = default; var currentValues = new List(); @@ -89,7 +104,12 @@ namespace ShardingCore.Sharding.Enumerators currentValues.Add(current); var first = _queue.Poll(); +#if !EFCORE2 if (await first.MoveNextAsync()) +#endif +#if EFCORE2 + if (await first.MoveNext(cancellationToken)) +#endif { _queue.Offer(first); } @@ -203,6 +223,7 @@ namespace ShardingCore.Sharding.Enumerators return CurrentValue; } +#if !EFCORE2 public async ValueTask DisposeAsync() { @@ -211,6 +232,7 @@ namespace ShardingCore.Sharding.Enumerators await enumerator.DisposeAsync(); } } +#endif public void Reset() diff --git a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/MultiOrderStreamMergeAsyncEnumerator.cs b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/MultiOrderStreamMergeAsyncEnumerator.cs index f7bf0c61..c7bdeb9b 100644 --- a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/MultiOrderStreamMergeAsyncEnumerator.cs +++ b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/MultiOrderStreamMergeAsyncEnumerator.cs @@ -45,7 +45,12 @@ namespace ShardingCore.Sharding.Enumerators.StreamMergeAsync _currentEnumerator = _queue.IsEmpty() ? _enumerators.FirstOrDefault() : _queue.Peek(); } +#if !EFCORE2 public async ValueTask MoveNextAsync() +#endif +#if EFCORE2 + public async Task MoveNext(CancellationToken cancellationToken = new CancellationToken()) +#endif { if (_queue.IsEmpty()) return false; @@ -56,8 +61,14 @@ namespace ShardingCore.Sharding.Enumerators.StreamMergeAsync } var first = _queue.Poll(); +#if !EFCORE2 if (await first.MoveNextAsync()) +#endif +#if EFCORE2 + + if (await first.MoveNext(cancellationToken)) +#endif { _queue.Offer(first); } @@ -117,6 +128,7 @@ namespace ShardingCore.Sharding.Enumerators.StreamMergeAsync return _currentEnumerator.GetCurrent(); } +#if !EFCORE2 public async ValueTask DisposeAsync() { @@ -125,6 +137,7 @@ namespace ShardingCore.Sharding.Enumerators.StreamMergeAsync await enumerator.DisposeAsync(); } } +#endif diff --git a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/OrderStreamMergeAsyncEnumerator.cs b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/OrderStreamMergeAsyncEnumerator.cs index 4b5b96fa..3b8633a4 100644 --- a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/OrderStreamMergeAsyncEnumerator.cs +++ b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/OrderStreamMergeAsyncEnumerator.cs @@ -38,9 +38,19 @@ namespace ShardingCore.Sharding.Enumerators _orderValues = HasElement() ? GetCurrentOrderValues() : new List(0); } +#if !EFCORE2 public async ValueTask MoveNextAsync() +#endif +#if EFCORE2 + public async Task MoveNext(CancellationToken cancellationToken = new CancellationToken()) +#endif { +#if !EFCORE2 var has = await _enumerator.MoveNextAsync(); +#endif +#if EFCORE2 + var has = await _enumerator.MoveNext(cancellationToken); +#endif SetOrderValues(); return has; } @@ -119,10 +129,12 @@ namespace ShardingCore.Sharding.Enumerators { return _orderValues ?? new List(0); } +#if !EFCORE2 public ValueTask DisposeAsync() { return _enumerator.DisposeAsync(); } +#endif } } \ No newline at end of file diff --git a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/PaginationStreamMergeAsyncEnumerator.cs b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/PaginationStreamMergeAsyncEnumerator.cs index 43baa54f..80b6401a 100644 --- a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/PaginationStreamMergeAsyncEnumerator.cs +++ b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/PaginationStreamMergeAsyncEnumerator.cs @@ -31,19 +31,34 @@ namespace ShardingCore.Sharding.Enumerators.StreamMergeAsync else _enumerator = new MultiOrderStreamMergeAsyncEnumerator(_mergeContext, sources); } +#if !EFCORE2 public async ValueTask MoveNextAsync() +#endif +#if EFCORE2 + public async Task MoveNext(CancellationToken cancellationToken = new CancellationToken()) +#endif { //如果合并数据的时候不需要跳过也没有take多少那么就是直接next while (_skip.GetValueOrDefault() > this.realSkip) { +#if !EFCORE2 var has = await _enumerator.MoveNextAsync(); +#endif +#if EFCORE2 + var has = await _enumerator.MoveNext(cancellationToken); +#endif realSkip++; if (!has) return false; } +#if !EFCORE2 var next = await _enumerator.MoveNextAsync(); +#endif +#if EFCORE2 + var next = await _enumerator.MoveNext(cancellationToken); +#endif if (next) { @@ -111,9 +126,12 @@ namespace ShardingCore.Sharding.Enumerators.StreamMergeAsync { _enumerator.Dispose(); } +#if !EFCORE2 + public ValueTask DisposeAsync() { return _enumerator.DisposeAsync(); } +#endif } } \ No newline at end of file diff --git a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/StreamMergeAsyncEnumerator.cs b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/StreamMergeAsyncEnumerator.cs index be8e781f..faa0e495 100644 --- a/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/StreamMergeAsyncEnumerator.cs +++ b/src/ShardingCore/Sharding/Enumerators/StreamMergeAsync/StreamMergeAsyncEnumerator.cs @@ -43,6 +43,7 @@ namespace ShardingCore.Sharding.Enumerators } return false; } +#if !EFCORE2 public async ValueTask DisposeAsync() { if (_asyncSource != null) @@ -95,6 +96,7 @@ namespace ShardingCore.Sharding.Enumerators return _syncSource.MoveNext(); } +#endif @@ -104,5 +106,69 @@ namespace ShardingCore.Sharding.Enumerators } object IEnumerator.Current => Current; +#if EFCORE2 + public void Dispose() + { + _asyncSource.Dispose(); + } + + public async Task MoveNext(CancellationToken cancellationToken = new CancellationToken()) + { + if (skip) + { + skip = false; + return null != SourceCurrent(); + } + return await _asyncSource.MoveNext(cancellationToken); + } + public T Current => GetCurrent(); + public T ReallyCurrent => GetReallyCurrent(); + public bool HasElement() + { + return null != SourceCurrent(); + } + + private T SourceCurrent() + { + try + { + if (tryGetCurrentError) + return default; + return _asyncSource.Current; + } + catch (Exception e) + { + tryGetCurrentError = true; + return default; + } + } + + private bool tryGetCurrentError = false; + + public T GetCurrent() + { + if (skip) + return default; + if (_asyncSource != null) return SourceCurrent(); + if (_syncSource != null) return _syncSource.Current; + return default; + } + public T GetReallyCurrent() + { + if (_asyncSource != null) return SourceCurrent(); + if (_syncSource != null) return _syncSource.Current; + return default; + } + public bool MoveNext() + { + if (skip) + { + skip = false; + return null != _syncSource.Current; + } + return _syncSource.MoveNext(); + } + +#endif } } \ No newline at end of file diff --git a/src/ShardingCore/Sharding/ShardingDbContextExecutor.cs b/src/ShardingCore/Sharding/ShardingDbContextExecutor.cs index 82043e4f..5d739d34 100644 --- a/src/ShardingCore/Sharding/ShardingDbContextExecutor.cs +++ b/src/ShardingCore/Sharding/ShardingDbContextExecutor.cs @@ -226,7 +226,12 @@ namespace ShardingCore.Sharding { foreach (var keyValuePair in dbContextCache.Value) { +#if EFCORE2 + keyValuePair.Value.Database.UseTransaction(null); +#endif +#if !EFCORE2 await keyValuePair.Value.Database.UseTransactionAsync(null, cancellationToken); +#endif } } this.CurrentShardingTransaction = null; @@ -274,6 +279,7 @@ namespace ShardingCore.Sharding } } +#if !EFCORE2 public async ValueTask DisposeAsync() { @@ -285,5 +291,6 @@ namespace ShardingCore.Sharding } } } +#endif } } diff --git a/src/ShardingCore/Sharding/ShardingQueryExecutors/DefaultShardingQueryExecutor.cs b/src/ShardingCore/Sharding/ShardingQueryExecutors/DefaultShardingQueryExecutor.cs index 556910bb..2cc9b4ba 100644 --- a/src/ShardingCore/Sharding/ShardingQueryExecutors/DefaultShardingQueryExecutor.cs +++ b/src/ShardingCore/Sharding/ShardingQueryExecutors/DefaultShardingQueryExecutor.cs @@ -12,6 +12,9 @@ using ShardingCore.Sharding.Abstractions; using ShardingCore.Sharding.StreamMergeEngines; using ShardingCore.Sharding.StreamMergeEngines.Abstractions; using ShardingCore.Sharding.StreamMergeEngines.AggregateMergeEngines; +#if EFCORE2 +using Microsoft.EntityFrameworkCore.Internal; +#endif namespace ShardingCore.Sharding.ShardingQueryExecutors { diff --git a/src/ShardingCore/Sharding/ShardingTransactions/IShardingTransaction.cs b/src/ShardingCore/Sharding/ShardingTransactions/IShardingTransaction.cs index 90d5a0fc..cfe044d5 100644 --- a/src/ShardingCore/Sharding/ShardingTransactions/IShardingTransaction.cs +++ b/src/ShardingCore/Sharding/ShardingTransactions/IShardingTransaction.cs @@ -13,14 +13,19 @@ namespace ShardingCore.Sharding.ShardingTransactions * @Ver: 1.0 * @Email: 326308290@qq.com */ - public interface IShardingTransaction:IDisposable,IAsyncDisposable + public interface IShardingTransaction:IDisposable +#if !EFCORE2 +,IAsyncDisposable +#endif { bool IsBeginTransaction(); void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified); void Use(string dataSourceName,DbContext dbContext); void Rollback(); - Task RollbackAsync(CancellationToken cancellationToken=new CancellationToken()); void Commit(); +#if !EFCORE2 + Task RollbackAsync(CancellationToken cancellationToken = new CancellationToken()); Task CommitAsync(CancellationToken cancellationToken = new CancellationToken()); +#endif } } diff --git a/src/ShardingCore/Sharding/ShardingTransactions/ShardingTransaction.cs b/src/ShardingCore/Sharding/ShardingTransactions/ShardingTransaction.cs index e64ae3ae..9bd5c884 100644 --- a/src/ShardingCore/Sharding/ShardingTransactions/ShardingTransaction.cs +++ b/src/ShardingCore/Sharding/ShardingTransactions/ShardingTransaction.cs @@ -19,7 +19,7 @@ namespace ShardingCore.Sharding.ShardingTransactions * @Ver: 1.0 * @Email: 326308290@qq.com */ - public class ShardingTransaction: IShardingTransaction + public class ShardingTransaction : IShardingTransaction { private readonly IShardingDbContextExecutor _shardingDbContextExecutor; @@ -28,7 +28,7 @@ namespace ShardingCore.Sharding.ShardingTransactions private IsolationLevel isolationLevel = IsolationLevel.Unspecified; - private bool _isBeginTransaction=false; + private bool _isBeginTransaction = false; public ShardingTransaction(IShardingDbContextExecutor shardingDbContextExecutor) { @@ -84,22 +84,6 @@ namespace ShardingCore.Sharding.ShardingTransactions this._shardingDbContextExecutor.ClearTransaction(); } - public async Task RollbackAsync(CancellationToken cancellationToken = new CancellationToken()) - { - foreach (var dbContextTransaction in _dbContextTransactions) - { - try - { - await dbContextTransaction.Value.RollbackAsync(cancellationToken); - } - catch (Exception e) - { - Console.WriteLine($"rollback error:[{e}]"); - } - } - await this._shardingDbContextExecutor.ClearTransactionAsync(cancellationToken); - } - public void Commit() { foreach (var dbContextTransaction in _dbContextTransactions) @@ -116,7 +100,24 @@ namespace ShardingCore.Sharding.ShardingTransactions this._shardingDbContextExecutor.ClearTransaction(); } - public async Task CommitAsync(CancellationToken cancellationToken = new CancellationToken()) +#if !EFCORE2 + + public async Task RollbackAsync(CancellationToken cancellationToken = new CancellationToken()) + { + foreach (var dbContextTransaction in _dbContextTransactions) + { + try + { + await dbContextTransaction.Value.RollbackAsync(cancellationToken); + } + catch (Exception e) + { + Console.WriteLine($"rollback error:[{e}]"); + } + } + await this._shardingDbContextExecutor.ClearTransactionAsync(cancellationToken); + } + public async Task CommitAsync(CancellationToken cancellationToken = new CancellationToken()) { foreach (var dbContextTransaction in _dbContextTransactions) { @@ -131,24 +132,6 @@ namespace ShardingCore.Sharding.ShardingTransactions } await this._shardingDbContextExecutor.ClearTransactionAsync(cancellationToken); } - - public void Dispose() - { - - foreach (var dbContextTransaction in _dbContextTransactions) - { - try - { - dbContextTransaction.Value.Dispose(); - } - catch (Exception e) - { - Console.WriteLine($"dispose error:[{e}]"); - } - } - _dbContextTransactions.Clear(); - } - public async ValueTask DisposeAsync() { foreach (var dbContextTransaction in _dbContextTransactions) @@ -164,5 +147,24 @@ namespace ShardingCore.Sharding.ShardingTransactions } _dbContextTransactions.Clear(); } +#endif + + public void Dispose() + { + + foreach (var dbContextTransaction in _dbContextTransactions) + { + try + { + dbContextTransaction.Value.Dispose(); + } + catch (Exception e) + { + Console.WriteLine($"dispose error:[{e}]"); + } + } + _dbContextTransactions.Clear(); + } + } } diff --git a/src/ShardingCore/Sharding/StreamMergeEngines/AsyncEnumerableStreamMergeEngine.cs b/src/ShardingCore/Sharding/StreamMergeEngines/AsyncEnumerableStreamMergeEngine.cs index f1092745..e9363f65 100644 --- a/src/ShardingCore/Sharding/StreamMergeEngines/AsyncEnumerableStreamMergeEngine.cs +++ b/src/ShardingCore/Sharding/StreamMergeEngines/AsyncEnumerableStreamMergeEngine.cs @@ -21,12 +21,22 @@ namespace ShardingCore.Sharding.StreamMergeEngines } +#if !EFCORE2 public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken()) { return new EnumeratorShardingQueryExecutor(_mergeContext).ExecuteAsync(cancellationToken) .GetAsyncEnumerator(cancellationToken); } - +#endif + +#if EFCORE2 + IAsyncEnumerator IAsyncEnumerable.GetEnumerator() + { + return ((IAsyncEnumerable)new EnumeratorShardingQueryExecutor(_mergeContext).ExecuteAsync()) + .GetEnumerator(); + } +#endif + public IEnumerator GetEnumerator() { diff --git a/src/ShardingCore/Sharding/StreamMergeEngines/EnumeratorStreamMergeEngines/Abstractions/AbstractEnumeratorAsyncStreamMergeEngine.cs b/src/ShardingCore/Sharding/StreamMergeEngines/EnumeratorStreamMergeEngines/Abstractions/AbstractEnumeratorAsyncStreamMergeEngine.cs index 8adffc0d..3a0eeefb 100644 --- a/src/ShardingCore/Sharding/StreamMergeEngines/EnumeratorStreamMergeEngines/Abstractions/AbstractEnumeratorAsyncStreamMergeEngine.cs +++ b/src/ShardingCore/Sharding/StreamMergeEngines/EnumeratorStreamMergeEngines/Abstractions/AbstractEnumeratorAsyncStreamMergeEngine.cs @@ -10,6 +10,9 @@ using ShardingCore.Exceptions; using ShardingCore.Extensions; using ShardingCore.Sharding.Enumerators; using ShardingCore.Sharding.Enumerators.StreamMergeAsync; +#if EFCORE2 +using Microsoft.EntityFrameworkCore.Extensions.Internal; +#endif namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.Abstractions { @@ -30,7 +33,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines. public abstract IStreamMergeAsyncEnumerator[] GetDbStreamMergeAsyncEnumerators(bool async); public abstract IStreamMergeAsyncEnumerator GetStreamMergeAsyncEnumerator(IStreamMergeAsyncEnumerator[] streamsAsyncEnumerators); - public Task> AsyncQueryEnumerator(IQueryable queryable,bool async) + public Task> AsyncQueryEnumerator(IQueryable queryable, bool async) { return Task.Run(async () => { @@ -43,7 +46,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines. } else { - var enumerator = DoGetEnumerator(queryable); + var enumerator = DoGetEnumerator(queryable); return new StreamMergeAsyncEnumerator(enumerator); } @@ -57,26 +60,33 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines. } public async Task> DoGetAsyncEnumerator(IQueryable newQueryable) { +#if !EFCORE2 var enumator = newQueryable.AsAsyncEnumerable().GetAsyncEnumerator(); await enumator.MoveNextAsync(); return enumator; +#endif +#if EFCORE2 + var enumator = newQueryable.AsAsyncEnumerable().GetEnumerator(); + await enumator.MoveNext(); + return enumator; +#endif } public IEnumerator DoGetEnumerator(IQueryable newQueryable) { var enumator = newQueryable.AsEnumerable().GetEnumerator(); - enumator.MoveNext(); + enumator.MoveNext(); return enumator; } - // public virtual IQueryable CreateAsyncExecuteQueryable(TableRouteResult tableRouteResult) + // public virtual IQueryable CreateAsyncExecuteQueryable(RouteResult routeResult) // { - // var shardingDbContext = StreamMergeContext.CreateDbContext(tableRouteResult); + // var shardingDbContext = StreamMergeContext.CreateDbContext(routeResult); // var useOriginal = StreamMergeContext > 1; - // DbContextQueryStore.TryAdd(tableRouteResult,shardingDbContext); + // DbContextQueryStore.TryAdd(routeResult,shardingDbContext); // var newQueryable = (IQueryable)(useOriginal ? StreamMergeContext.GetReWriteQueryable() : StreamMergeContext.GetOriginalQueryable()) // .ReplaceDbContextQueryable(shardingDbContext); // return newQueryable; // } - + public override IStreamMergeAsyncEnumerator GetShardingAsyncEnumerator(bool async, CancellationToken cancellationToken = new CancellationToken()) { diff --git a/src/ShardingCore/Sharding/StreamMergeEngines/EnumeratorStreamMergeEngines/Abstractions/AbstractEnumeratorStreamMergeEngine.cs b/src/ShardingCore/Sharding/StreamMergeEngines/EnumeratorStreamMergeEngines/Abstractions/AbstractEnumeratorStreamMergeEngine.cs index b680c22e..6da9d8fb 100644 --- a/src/ShardingCore/Sharding/StreamMergeEngines/EnumeratorStreamMergeEngines/Abstractions/AbstractEnumeratorStreamMergeEngine.cs +++ b/src/ShardingCore/Sharding/StreamMergeEngines/EnumeratorStreamMergeEngines/Abstractions/AbstractEnumeratorStreamMergeEngine.cs @@ -18,10 +18,10 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines. * @Ver: 1.0 * @Email: 326308290@qq.com */ - public abstract class AbstractEnumeratorStreamMergeEngine:IEnumeratorStreamMergeEngine + public abstract class AbstractEnumeratorStreamMergeEngine : IEnumeratorStreamMergeEngine { public StreamMergeContext StreamMergeContext { get; } - public ConcurrentDictionary DbContextQueryStore { get; } + public ConcurrentDictionary DbContextQueryStore { get; } public AbstractEnumeratorStreamMergeEngine(StreamMergeContext streamMergeContext) { @@ -32,13 +32,23 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines. public abstract IStreamMergeAsyncEnumerator GetShardingAsyncEnumerator(bool async, CancellationToken cancellationToken = new CancellationToken()); +#if !EFCORE2 public IAsyncEnumerator GetAsyncEnumerator( CancellationToken cancellationToken = new CancellationToken()) { return GetShardingAsyncEnumerator(true,cancellationToken); } +#endif - public IEnumerator GetEnumerator() +#if EFCORE2 + IAsyncEnumerator IAsyncEnumerable.GetEnumerator() + { + return GetShardingAsyncEnumerator(true); + } + +#endif + + public IEnumerator GetEnumerator() { return GetShardingAsyncEnumerator(false); } diff --git a/src/ShardingCore/Sharding/Visitors/QueryableRouteDiscoverVisitor.cs b/src/ShardingCore/Sharding/Visitors/QueryableRouteDiscoverVisitor.cs index c6de6a23..68545b30 100644 --- a/src/ShardingCore/Sharding/Visitors/QueryableRouteDiscoverVisitor.cs +++ b/src/ShardingCore/Sharding/Visitors/QueryableRouteDiscoverVisitor.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Linq; using System.Linq.Expressions; +using ShardingCore.Core.VirtualDatabase; using ShardingCore.Core.VirtualRoutes; using ShardingCore.Core.VirtualTables; using ShardingCore.Exceptions; @@ -17,12 +18,12 @@ namespace ShardingCore.Core.Internal.Visitors */ public class QueryableRouteShardingTableDiscoverVisitor : ExpressionVisitor { - private readonly ShardingTableConfig _shardingConfig; + private readonly ShardingEntityConfig _shardingConfig; private readonly Func _shardingKeyConvert; private readonly Func>> _keyToTailWithFilter; private Expression> _where = x => true; - public QueryableRouteShardingTableDiscoverVisitor(ShardingTableConfig shardingConfig, Func shardingKeyConvert, Func>> keyToTailWithFilter) + public QueryableRouteShardingTableDiscoverVisitor(ShardingEntityConfig shardingConfig, Func shardingKeyConvert, Func>> keyToTailWithFilter) { _shardingConfig = shardingConfig; _shardingKeyConvert = shardingKeyConvert; @@ -37,8 +38,8 @@ namespace ShardingCore.Core.Internal.Visitors private bool IsShardingKey(Expression expression) { return expression is MemberExpression member - && member.Expression.Type == _shardingConfig.ShardingEntityType - && member.Member.Name == _shardingConfig.ShardingField; + && member.Expression.Type == _shardingConfig.EntityType + && member.Member.Name == _shardingConfig.ShardingTableField; } /// /// 方法是否包含shardingKey @@ -52,8 +53,8 @@ namespace ShardingCore.Core.Internal.Visitors for (int i = 0; i < methodCallExpression.Arguments.Count; i++) { var isShardingKey = methodCallExpression.Arguments[i] is MemberExpression member - && member.Expression.Type == _shardingConfig.ShardingEntityType - && member.Member.Name == _shardingConfig.ShardingField; + && member.Expression.Type == _shardingConfig.EntityType + && member.Member.Name == _shardingConfig.ShardingTableField; if (isShardingKey) return true; } } diff --git a/src/ShardingCore/ShardingDbContextBootstrapper.cs b/src/ShardingCore/ShardingDbContextBootstrapper.cs index aead7237..8f8e1353 100644 --- a/src/ShardingCore/ShardingDbContextBootstrapper.cs +++ b/src/ShardingCore/ShardingDbContextBootstrapper.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase; using ShardingCore.Core.VirtualDatabase.VirtualDataSources; using ShardingCore.Core.VirtualDatabase.VirtualDataSources.PhysicDataSources; using ShardingCore.Core.VirtualDatabase.VirtualTables; @@ -85,8 +86,13 @@ namespace ShardingCore var virtualTable = CreateVirtualTable(entityType, virtualRoute); //获取ShardingEntity的实际表名 +#if !EFCORE2 var tableName = context.Model.FindEntityType(virtualTable.EntityType).GetTableName(); - virtualTable.SetOriginalTableName(tableName); +#endif +#if EFCORE2 + var tableName = context.Model.FindEntityType(virtualTable.EntityType).Relational().TableName; +#endif + virtualTable.SetVirtualTableName(tableName); _virtualTableManager.AddVirtualTable(virtualTable); CreateDataTable(dataSourceName,virtualTable); } @@ -174,7 +180,7 @@ namespace ShardingCore return (IVirtualTable)o; } - private bool NeedCreateTable(ShardingTableConfig config) + private bool NeedCreateTable(ShardingEntityConfig config) { if (config.AutoCreateTable.HasValue) { diff --git a/src/ShardingCore/Utils/ShardingKeyUtil.cs b/src/ShardingCore/Utils/ShardingKeyUtil.cs index da1440fa..6cf18875 100644 --- a/src/ShardingCore/Utils/ShardingKeyUtil.cs +++ b/src/ShardingCore/Utils/ShardingKeyUtil.cs @@ -1,88 +1,72 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using ShardingCore.Core; -using ShardingCore.Core.Internal.Visitors; -using ShardingCore.Core.Internal.Visitors.Querys; -using ShardingCore.Core.VirtualRoutes; -using ShardingCore.Core.VirtualTables; +//using System; +//using System.Collections.Concurrent; +//using System.Collections.Generic; +//using System.Linq; +//using System.Linq.Expressions; +//using System.Reflection; +//using ShardingCore.Core; +//using ShardingCore.Core.Internal.Visitors; +//using ShardingCore.Core.Internal.Visitors.Querys; +//using ShardingCore.Core.VirtualRoutes; +//using ShardingCore.Core.VirtualTables; -namespace ShardingCore.Utils -{ -/* -* @Author: xjm -* @Description: -* @Date: Saturday, 19 December 2020 20:20:29 -* @Email: 326308290@qq.com -*/ - public class ShardingKeyUtil - { - private static readonly ConcurrentDictionary _caches = new ConcurrentDictionary(); +//namespace ShardingCore.Utils +//{ +///* +//* @Author: xjm +//* @Description: +//* @Date: Saturday, 19 December 2020 20:20:29 +//* @Email: 326308290@qq.com +//*/ +// public class ShardingKeyUtil +// { +// private static readonly ConcurrentDictionary _caches = new ConcurrentDictionary(); - private ShardingKeyUtil() - { - } +// private ShardingKeyUtil() +// { +// } - public static ShardingTableConfig Parse(Type entityType) - { - if (!typeof(IShardingTable).IsAssignableFrom(entityType)) - throw new NotSupportedException(entityType.ToString()); - if (_caches.TryGetValue(entityType, out var shardingEntityConfig)) - { - return shardingEntityConfig; - } +// public static ShardingTableConfig Parse(Type entityType) +// { +// if (!typeof(IShardingTable).IsAssignableFrom(entityType)) +// throw new NotSupportedException(entityType.ToString()); +// if (_caches.TryGetValue(entityType, out var shardingEntityConfig)) +// { +// return shardingEntityConfig; +// } - PropertyInfo[] shardingProperties = entityType.GetProperties(); - foreach (var shardingProperty in shardingProperties) - { - var attribbutes = shardingProperty.GetCustomAttributes(true); - if (attribbutes.FirstOrDefault(x => x.GetType() == typeof(ShardingTableKeyAttribute)) is ShardingTableKeyAttribute shardingKeyAttribute) - { - if (shardingEntityConfig != null) - throw new ArgumentException($"{entityType} found more than one [ShardingKeyAttribute]"); - shardingEntityConfig = new ShardingTableConfig() - { - ShardingEntityType = entityType, - ShardingField = shardingProperty.Name, - AutoCreateTable = shardingKeyAttribute.AutoCreateTableOnStart==ShardingKeyAutoCreateTableEnum.UnKnown?(bool?)null:(shardingKeyAttribute.AutoCreateTableOnStart==ShardingKeyAutoCreateTableEnum.Create), - TailPrefix = shardingKeyAttribute.TailPrefix - }; - _caches.TryAdd(entityType, shardingEntityConfig); - } - } +// PropertyInfo[] shardingProperties = entityType.GetProperties(); +// foreach (var shardingProperty in shardingProperties) +// { +// var attribbutes = shardingProperty.GetCustomAttributes(true); +// if (attribbutes.FirstOrDefault(x => x.GetType() == typeof(ShardingTableKeyAttribute)) is ShardingTableKeyAttribute shardingKeyAttribute) +// { +// if (shardingEntityConfig != null) +// throw new ArgumentException($"{entityType} found more than one [ShardingKeyAttribute]"); +// shardingEntityConfig = new ShardingTableConfig() +// { +// ShardingEntityType = entityType, +// ShardingField = shardingProperty.Name, +// AutoCreateTable = shardingKeyAttribute.AutoCreateTableOnStart==ShardingKeyAutoCreateTableEnum.UnKnown?(bool?)null:(shardingKeyAttribute.AutoCreateTableOnStart==ShardingKeyAutoCreateTableEnum.Create), +// TailPrefix = shardingKeyAttribute.TailPrefix +// }; +// _caches.TryAdd(entityType, shardingEntityConfig); +// } +// } - return shardingEntityConfig; - } - - public static Func GetRouteShardingTableFilter(IQueryable queryable, ShardingTableConfig shardingConfig, Func shardingKeyConvert, Func>> keyToTailExpression) - { - QueryableRouteShardingTableDiscoverVisitor visitor = new QueryableRouteShardingTableDiscoverVisitor(shardingConfig, shardingKeyConvert, keyToTailExpression); - - visitor.Visit(queryable.Expression); - - return visitor.GetStringFilterTail(); - } +// return shardingEntityConfig; +// } - public static ISet GetShardingEntitiesFilter(IQueryable queryable) - { - ShardingEntitiesVisitor visitor = new ShardingEntitiesVisitor(); - visitor.Visit(queryable.Expression); +// //public static ISet GetShardingEntitiesFilter(IQueryable queryable) +// //{ +// // ShardingEntitiesVisitor visitor = new ShardingEntitiesVisitor(); - return visitor.GetShardingEntities(); - } - public static ISet GetQueryEntitiesFilter(IQueryable queryable) - { - QueryEntitiesVisitor visitor = new QueryEntitiesVisitor(); +// // visitor.Visit(queryable.Expression); - visitor.Visit(queryable.Expression); +// // return visitor.GetShardingEntities(); +// //} - return visitor.GetQueryEntities(); - } - - } -} \ No newline at end of file +// } +//} \ No newline at end of file diff --git a/src/ShardingCore/Utils/ShardingUtil.cs b/src/ShardingCore/Utils/ShardingUtil.cs index 44cd4502..7e08a819 100644 --- a/src/ShardingCore/Utils/ShardingUtil.cs +++ b/src/ShardingCore/Utils/ShardingUtil.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using ShardingCore.Core; using ShardingCore.Core.Internal; using ShardingCore.Core.Internal.Visitors; +using ShardingCore.Core.Internal.Visitors.Querys; using ShardingCore.Core.VirtualDatabase; using ShardingCore.Core.VirtualRoutes; using ShardingCore.Extensions; @@ -28,13 +30,16 @@ namespace ShardingCore.Utils public static ShardingEntityConfig Parse(Type entityType) { + var isShardingTable = entityType.IsShardingTable(); + var isShardingDataSource = entityType.IsShardingDataSource(); + if (!isShardingTable && !isShardingDataSource) + throw new InvalidOperationException( + $"{entityType.FullName} is not impl {nameof(IShardingDataSource)} or {nameof(IShardingTable)}"); if (_caches.TryGetValue(entityType, out var entityConfig)) { return entityConfig; } - var isShardingTable = entityType.IsShardingTable(); - var isShardingDataSource = entityType.IsShardingDataSource(); entityConfig = new ShardingEntityConfig() { EntityType = entityType, @@ -43,17 +48,8 @@ namespace ShardingCore.Utils }; - //if (!isShardingDataSource && isShardingTable) - // throw new NotSupportedException(entityType.FullName); - PropertyInfo[] shardingProperties = entityType.GetProperties(); - - //if (isShardingTable) - //{ - // var shardingTables = shardingProperties.SelectMany(p => p.GetCustomAttributes(true).Where(o => o.GetType() == typeof(ShardingTableKeyAttribute))).ToList(); - // if (shardingTables.Count != 1) - // throw new NotSupportedException($"{entityType} From IShardingTable should use single attribute [{nameof(ShardingTableKeyAttribute)}]"); - //} + var shardingDataSourceCount = 0; var shardingTableCount = 0; @@ -65,7 +61,7 @@ namespace ShardingCore.Utils if (attributes.FirstOrDefault(x => x.GetType() == typeof(ShardingDataSourceKeyAttribute)) is ShardingDataSourceKeyAttribute shardingDataSourceKey) { if (shardingDataSourceCount > 1) - throw new NotSupportedException($"{entityType} From IShardingDataSource should use single attribute [{nameof(ShardingDataSourceKeyAttribute)}]"); + throw new NotSupportedException($"{entityType} impl {nameof(IShardingDataSource)} should use single attribute [{nameof(ShardingDataSourceKeyAttribute)}]"); entityConfig.ShardingDataSourceField = shardingProperty.Name; entityConfig.AutoCreateDataSource = shardingDataSourceKey.AutoCreateDataSourceOnStart == ShardingKeyAutoCreateDataSourceEnum.UnKnown ? (bool?)null : (shardingDataSourceKey.AutoCreateDataSourceOnStart == ShardingKeyAutoCreateDataSourceEnum.Create); @@ -78,7 +74,7 @@ namespace ShardingCore.Utils if (attributes.FirstOrDefault(x => x.GetType() == typeof(ShardingTableKeyAttribute)) is ShardingTableKeyAttribute shardingKey) { if (shardingTableCount > 1) - throw new NotSupportedException($"{entityType} From IShardingTable should use single attribute [{nameof(ShardingTableKeyAttribute)}]"); + throw new NotSupportedException($"{entityType} impl {nameof(IShardingTable)} should use single attribute [{nameof(ShardingTableKeyAttribute)}]"); entityConfig.ShardingTableField = shardingProperty.Name; entityConfig.AutoCreateTable = shardingKey.AutoCreateTableOnStart == ShardingKeyAutoCreateTableEnum.UnKnown ? (bool?) null : (shardingKey.AutoCreateTableOnStart == ShardingKeyAutoCreateTableEnum.Create); @@ -93,7 +89,15 @@ namespace ShardingCore.Utils return entityConfig; } - + /// + /// ֿ·ɹ + /// + /// + /// + /// + /// + /// + /// public static Func GetRouteDataSourceFilter(IQueryable queryable, ShardingEntityConfig shardingEntityBaseType, Func shardingKeyConvert, Func>> keyToTailExpression) { QueryableRouteShardingDataSourceDiscoverVisitor visitor = new QueryableRouteShardingDataSourceDiscoverVisitor(shardingEntityBaseType, shardingKeyConvert, keyToTailExpression); @@ -102,6 +106,36 @@ namespace ShardingCore.Utils return visitor.GetDataSourceFilter(); } + /// + /// ֱ·ɹ + /// + /// + /// + /// + /// + /// + /// + public static Func GetRouteShardingTableFilter(IQueryable queryable, ShardingEntityConfig shardingConfig, Func shardingKeyConvert, Func>> keyToTailExpression) + { + QueryableRouteShardingTableDiscoverVisitor visitor = new QueryableRouteShardingTableDiscoverVisitor(shardingConfig, shardingKeyConvert, keyToTailExpression); + + visitor.Visit(queryable.Expression); + + return visitor.GetStringFilterTail(); + } + /// + /// ȡβѯ漰Ķ + /// + /// + /// + public static ISet GetQueryEntitiesFilter(IQueryable queryable) + { + QueryEntitiesVisitor visitor = new QueryEntitiesVisitor(); + + visitor.Visit(queryable.Expression); + + return visitor.GetQueryEntities(); + } } } \ No newline at end of file diff --git a/src2x/ShardingCore.2x/ShardingCore.2x.csproj b/src2x/ShardingCore.2x/ShardingCore.2x.csproj new file mode 100644 index 00000000..e526af23 --- /dev/null +++ b/src2x/ShardingCore.2x/ShardingCore.2x.csproj @@ -0,0 +1,36 @@ + + + + + netstandard2.0 + $(EFCORE2) + true + TRACE;DEBUG;EFCORE2; + 8.0 + ShardingCore + ShardingCore + + + + true + bin\Release\ShardingCore.2x.xml + + + + + + + + + + + + + + + + + + + + diff --git a/test/ShardingCore.Test50.MySql/DefaultDbContext.cs b/test/ShardingCore.Test50.MySql/DefaultDbContext.cs index 4df9bac8..f6a60c07 100644 --- a/test/ShardingCore.Test50.MySql/DefaultDbContext.cs +++ b/test/ShardingCore.Test50.MySql/DefaultDbContext.cs @@ -1,5 +1,5 @@ using Microsoft.EntityFrameworkCore; -using ShardingCore.Core.VirtualRoutes.RouteTails.Abstractions; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; using ShardingCore.Sharding.Abstractions; using ShardingCore.Test50.MySql.Domain.Maps; diff --git a/test/ShardingCore.Test50/Startup.cs b/test/ShardingCore.Test50/Startup.cs index 74b30662..5a613e81 100644 --- a/test/ShardingCore.Test50/Startup.cs +++ b/test/ShardingCore.Test50/Startup.cs @@ -50,7 +50,7 @@ namespace ShardingCore.Test50 { services.AddShardingDbContext(o => o.UseSqlServer(hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"])) - .Begin(true) + .Begin(true,true) .AddShardingQuery((conStr, builder) => builder.UseSqlServer(conStr).UseLoggerFactory(efLogger)) .AddShardingTransaction((connection, builder) =>builder.UseSqlServer(connection).UseLoggerFactory(efLogger)) .AddDefaultDataSource("ds0",hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"]) diff --git a/test/ShardingCore.Test50_2x/Configs/DbConfig.json b/test/ShardingCore.Test50_2x/Configs/DbConfig.json new file mode 100644 index 00000000..f305cd6d --- /dev/null +++ b/test/ShardingCore.Test50_2x/Configs/DbConfig.json @@ -0,0 +1,5 @@ +{ + "SqlServer": { + "ConnectionString": "Data Source=localhost;Initial Catalog=ShardingCoreDB;Integrated Security=True;" + } +} \ No newline at end of file diff --git a/test/ShardingCore.Test50_2x/DefaultDbContext.cs b/test/ShardingCore.Test50_2x/DefaultDbContext.cs new file mode 100644 index 00000000..f4cec207 --- /dev/null +++ b/test/ShardingCore.Test50_2x/DefaultDbContext.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; +using ShardingCore.Sharding.Abstractions; +using ShardingCore.Test50_2x.Domain.Maps; + +namespace ShardingCore.Test50_2x +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/3/31 15:28:11 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class DefaultDbContext : DbContext, IShardingTableDbContext + { + public DefaultDbContext(DbContextOptions options) : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.ApplyConfiguration(new SysUserModMap()); + modelBuilder.ApplyConfiguration(new SysUserSalaryMap()); + } + + public IRouteTail RouteTail { get; set; } + } +} diff --git a/test/ShardingCore.Test50_2x/Domain/Entities/SysUserMod.cs b/test/ShardingCore.Test50_2x/Domain/Entities/SysUserMod.cs new file mode 100644 index 00000000..cdb1eb32 --- /dev/null +++ b/test/ShardingCore.Test50_2x/Domain/Entities/SysUserMod.cs @@ -0,0 +1,29 @@ +using ShardingCore.Core; + +namespace ShardingCore.Test50_2x.Domain.Entities +{ +/* +* @Author: xjm +* @Description: +* @Date: Thursday, 14 January 2021 15:36:43 +* @Email: 326308290@qq.com +*/ + public class SysUserMod:IShardingTable + { + /// + /// 用户Id用于分表 + /// + [ShardingTableKey(TailPrefix = "_")] + public string Id { get; set; } + /// + /// 用户名称 + /// + public string Name { get; set; } + /// + /// 用户姓名 + /// + public int Age { get; set; } + public int AgeGroup { get; set; } + + } +} \ No newline at end of file diff --git a/test/ShardingCore.Test50_2x/Domain/Entities/SysUserSalary.cs b/test/ShardingCore.Test50_2x/Domain/Entities/SysUserSalary.cs new file mode 100644 index 00000000..e0dc6d9a --- /dev/null +++ b/test/ShardingCore.Test50_2x/Domain/Entities/SysUserSalary.cs @@ -0,0 +1,42 @@ +using ShardingCore.Core; + +namespace ShardingCore.Test50_2x.Domain.Entities +{ +/* +* @Author: xjm +* @Description: +* @Date: Monday, 01 February 2021 15:43:22 +* @Email: 326308290@qq.com +*/ + public class SysUserSalary:IShardingTable + { + public string Id { get; set; } + public string UserId { get; set; } + /// + /// 每月的金额 + /// + [ShardingTableKey] + public int DateOfMonth { get; set; } + /// + /// 工资 + /// + public int Salary { get; set; } + /// + /// 工资 + /// + public long SalaryLong { get; set; } + + /// + /// 工资 + /// + public decimal SalaryDecimal { get; set; } + /// + /// 工资 + /// + public double SalaryDouble { get; set; } + /// + /// 工资 + /// + public float SalaryFloat { get; set; } + } +} \ No newline at end of file diff --git a/test/ShardingCore.Test50_2x/Domain/Maps/SysUserModMap.cs b/test/ShardingCore.Test50_2x/Domain/Maps/SysUserModMap.cs new file mode 100644 index 00000000..07d78aa1 --- /dev/null +++ b/test/ShardingCore.Test50_2x/Domain/Maps/SysUserModMap.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test50_2x.Domain.Entities; + +namespace ShardingCore.Test50_2x.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.Test50_2x/Domain/Maps/SysUserSalaryMap.cs b/test/ShardingCore.Test50_2x/Domain/Maps/SysUserSalaryMap.cs new file mode 100644 index 00000000..a2feed71 --- /dev/null +++ b/test/ShardingCore.Test50_2x/Domain/Maps/SysUserSalaryMap.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using ShardingCore.Test50_2x.Domain.Entities; + +namespace ShardingCore.Test50_2x.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.Test50_2x/ShardingCore.Test50_2x.csproj b/test/ShardingCore.Test50_2x/ShardingCore.Test50_2x.csproj new file mode 100644 index 00000000..40bc3b0e --- /dev/null +++ b/test/ShardingCore.Test50_2x/ShardingCore.Test50_2x.csproj @@ -0,0 +1,31 @@ + + + + net5.0 + 9.0 + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + true + PreserveNewest + PreserveNewest + + + + + + + + \ No newline at end of file diff --git a/test/ShardingCore.Test50_2x/ShardingDefaultDbContext.cs b/test/ShardingCore.Test50_2x/ShardingDefaultDbContext.cs new file mode 100644 index 00000000..b1db7f59 --- /dev/null +++ b/test/ShardingCore.Test50_2x/ShardingDefaultDbContext.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.EntityFrameworkCore; +using ShardingCore.Sharding; +using ShardingCore.Test50_2x.Domain.Maps; + +namespace ShardingCore.Test50_2x +{ + /* + * @Author: xjm + * @Description: + * @Date: 2021/8/15 10:21:03 + * @Ver: 1.0 + * @Email: 326308290@qq.com + */ + public class ShardingDefaultDbContext:AbstractShardingDbContext + { + public ShardingDefaultDbContext(DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.ApplyConfiguration(new SysUserModMap()); + modelBuilder.ApplyConfiguration(new SysUserSalaryMap()); + } + + } +} diff --git a/test/ShardingCore.Test50_2x/ShardingTest.cs b/test/ShardingCore.Test50_2x/ShardingTest.cs new file mode 100644 index 00000000..01bbbce7 --- /dev/null +++ b/test/ShardingCore.Test50_2x/ShardingTest.cs @@ -0,0 +1,417 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using ShardingCore.Core.QueryRouteManagers.Abstractions; +using ShardingCore.Test50_2x.Domain.Entities; +using Xunit; + +namespace ShardingCore.Test50_2x +{ + /* + * @Author: xjm + * @Description: + * @Date: Friday, 15 January 2021 17:22:10 + * @Email: 326308290@qq.com + */ + public class ShardingTest + { + private readonly ShardingDefaultDbContext _virtualDbContext; + private readonly IShardingRouteManager _shardingRouteManager; + + public ShardingTest(ShardingDefaultDbContext virtualDbContext,IShardingRouteManager shardingRouteManager) + { + _virtualDbContext = virtualDbContext; + _shardingRouteManager = shardingRouteManager; + } + + //[Fact] + //public async Task Route_TEST() + //{ + // var queryable1 = _virtualDbContext.Set().Where(o=>o.Id=="339"); + // var routeResults1 = _routingRuleEngineFactory.Route(queryable1); + // Assert.Equal(1,routeResults1.Count()); + // Assert.Equal(1,routeResults1.FirstOrDefault().ReplaceTables.Count()); + // Assert.Equal("0",routeResults1.FirstOrDefault().ReplaceTables.FirstOrDefault().Tail); + // Assert.Equal(nameof(SysUserMod),routeResults1.FirstOrDefault().ReplaceTables.FirstOrDefault().OriginalName); + // var ids = new[] {"339", "124","142"}; + // var queryable2= _virtualDbContext.Set().Where(o=>ids.Contains(o.Id)); + // var routeResult2s = _routingRuleEngineFactory.Route(queryable2); + // Assert.Equal(2,routeResult2s.Count()); + // Assert.Equal(1,routeResult2s.FirstOrDefault().ReplaceTables.Count()); + // Assert.Equal(2,routeResult2s.SelectMany(o=>o.ReplaceTables).Count()); + // Assert.Equal(true,routeResult2s.SelectMany(o=>o.ReplaceTables).All(o=>new[]{"0","1"}.Contains(o.Tail))); + //} + + [Fact] + public async Task ToList_All_Route_Test() + { + using (_shardingRouteManager.CreateScope()) + { + _shardingRouteManager.Current.Must.TryAdd(typeof(SysUserMod), new HashSet() { "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--; + } + } + + [Fact] + public async Task ToList_Join_Test() + { + var list = await (from u in _virtualDbContext.Set() + join salary in _virtualDbContext.Set() + on u.Id equals salary.UserId + select new + { + 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"); + } + + [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 => ids.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 mods = await _virtualDbContext.Set().Where(o => o.Id == "3").ToListAsync(); + Assert.Single(mods); + Assert.Equal("3", mods[0].Id); + } + + [Fact] + public async Task ToList_Id_Not_Eq_Test() + { + var mods = await _virtualDbContext.Set().Where(o => o.Id != "3").ToListAsync(); + Assert.Equal(999, mods.Count); + Assert.DoesNotContain(mods, o => o.Id == "3"); + } + + [Fact] + public async Task ToList_Id_Not_Eq_Skip_Test() + { + var mods = await _virtualDbContext.Set().Where(o => o.Id != "3").OrderBy(o => o.Age).Skip(2).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 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 sysUserMod = await _virtualDbContext.Set().Where(o => o.Id == "1").FirstOrDefaultAsync(); + Assert.NotNull(sysUserMod); + 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); + } + + [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_API_Test() + // { + // var ids = new[] {"200", "300"}; + // var dateOfMonths = new[] {202111, 202110}; + // var group = await _virtualDbContext.Set() + // .Where(o => ids.Contains(o.UserId) && dateOfMonths.Contains(o.DateOfMonth)) + // .ShardingGroupByAsync(g => new {UId = g.UserId}, g => new + // { + // + // GroupUserId = g.Key.UId, + // Count = g.Count(), + // TotalSalary = g.Sum(o => o.Salary), + // AvgSalary = g.Average(o => o.Salary), + // AvgSalaryDecimal = g.Average(o => o.SalaryDecimal), + // MinSalary = g.Min(o => o.Salary), + // MaxSalary = g.Max(o => o.Salary) + // }); + // Assert.Equal(2, group.Count); + // Assert.Equal(2, group[0].Count); + // Assert.Equal(2260000, group[0].TotalSalary); + // Assert.Equal(1130000, group[0].AvgSalary); + // Assert.Equal(11300, group[0].AvgSalaryDecimal); + // Assert.Equal(1120000, group[0].MinSalary); + // Assert.Equal(1140000, group[0].MaxSalary); + // } + } +} \ No newline at end of file diff --git a/test/ShardingCore.Test50_2x/Shardings/SysUserModVirtualTableRoute.cs b/test/ShardingCore.Test50_2x/Shardings/SysUserModVirtualTableRoute.cs new file mode 100644 index 00000000..cb821e36 --- /dev/null +++ b/test/ShardingCore.Test50_2x/Shardings/SysUserModVirtualTableRoute.cs @@ -0,0 +1,20 @@ +using ShardingCore.Test50_2x.Domain.Entities; +using ShardingCore.VirtualRoutes.Mods; + +namespace ShardingCore.Test50_2x.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 SysUserModVirtualTableRoute() : base(2,3) + { + } + + } +} \ No newline at end of file diff --git a/test/ShardingCore.Test50_2x/Shardings/SysUserSalaryVirtualTableRoute.cs b/test/ShardingCore.Test50_2x/Shardings/SysUserSalaryVirtualTableRoute.cs new file mode 100644 index 00000000..87e5fbc0 --- /dev/null +++ b/test/ShardingCore.Test50_2x/Shardings/SysUserSalaryVirtualTableRoute.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Core.VirtualRoutes.TableRoutes; +using ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions; +using ShardingCore.Test50_2x.Domain.Entities; + +namespace ShardingCore.Test50_2x.Shardings +{ +/* +* @Author: xjm +* @Description: +* @Date: Monday, 01 February 2021 15:54:55 +* @Email: 326308290@qq.com +*/ + public class SysUserSalaryVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute + { + protected override int ConvertToShardingKey(object shardingKey) + { + return Convert.ToInt32(shardingKey); + } + + public override string ShardingKeyToTail(object shardingKey) + { + var time = ConvertToShardingKey(shardingKey); + return TimeFormatToTail(time); + } + + + public override List GetAllTails() + { + var beginTime = new DateTime(2020, 1, 1); + var endTime = new DateTime(2021, 12, 1); + var list = new List(24); + var tempTime = beginTime; + while (tempTime <= endTime) + { + list.Add($"{tempTime:yyyyMM}"); + tempTime = tempTime.AddMonths(1); + } + + return list; + } + + protected string TimeFormatToTail(int time) + { + var dateOfMonth=DateTime.ParseExact($"{time}","yyyyMM",System.Globalization.CultureInfo.InvariantCulture,System.Globalization.DateTimeStyles.AdjustToUniversal); + return $"{dateOfMonth:yyyyMM}"; + } + + protected override Expression> GetRouteToFilter(int shardingKey, ShardingOperatorEnum shardingOperator) + { + var t = TimeFormatToTail(shardingKey); + switch (shardingOperator) + { + case ShardingOperatorEnum.GreaterThan: + case ShardingOperatorEnum.GreaterThanOrEqual: + return tail => String.Compare(tail, t, StringComparison.Ordinal) >= 0; + case ShardingOperatorEnum.LessThan: + return tail => String.Compare(tail, t, StringComparison.Ordinal) < 0; + case ShardingOperatorEnum.LessThanOrEqual: + return tail => String.Compare(tail, t, StringComparison.Ordinal) <= 0; + case ShardingOperatorEnum.Equal: return tail => tail == t; + default: + { +#if DEBUG + Console.WriteLine($"shardingOperator is not equal scan all table tail"); +#endif + return tail => true; + } + } + } + } +} \ No newline at end of file diff --git a/test/ShardingCore.Test50_2x/Startup.cs b/test/ShardingCore.Test50_2x/Startup.cs new file mode 100644 index 00000000..3a2f37c9 --- /dev/null +++ b/test/ShardingCore.Test50_2x/Startup.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using ShardingCore.EFCores; +using ShardingCore.Test50_2x.Domain.Entities; +using ShardingCore.Test50_2x.Shardings; + +namespace ShardingCore.Test50_2x +{ + /* + * @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(); + }); + // // 自定义 host 构建 + public void ConfigureHost(IHostBuilder hostBuilder) + { + hostBuilder + .ConfigureAppConfiguration(builder => + { + builder.AddJsonFile("Configs/DbConfig.json"); + //builder.AddJsonFile("Configs/MacDbConfig.json"); + }); + } + + // 支持的形式: + // ConfigureServices(IServiceCollection services) + // ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext) + // ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services) + public void ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext) + { + //services.AddDbContext(); + + services.AddShardingDbContext(o => + o.UseSqlServer(hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"])) + .Begin(true, true) + .AddShardingQuery((conStr, builder) => builder.UseSqlServer(conStr).UseLoggerFactory(efLogger)) + .AddShardingTransaction((connection, builder) => builder.UseSqlServer(connection).UseLoggerFactory(efLogger)) + .AddDefaultDataSource("ds0", hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"]) + .AddShardingTable(op => + { + op.AddShardingTableRoute(); + op.AddShardingTableRoute(); + }).End(); + } + + // 可以添加要用到的方法参数,会自动从注册的服务中获取服务实例,类似于 asp.net core 里 Configure 方法 + public void Configure(IServiceProvider serviceProvider) + { + var shardingBootstrapper = serviceProvider.GetService(); + shardingBootstrapper.Start(); + // 有一些测试数据要初始化可以放在这里 + InitData(serviceProvider).GetAwaiter().GetResult(); + } + + /// + /// 添加种子数据 + /// + /// + /// + private async Task InitData(IServiceProvider serviceProvider) + { + using (var scope = serviceProvider.CreateScope()) + { + var virtualDbContext = scope.ServiceProvider.GetService(); + if (!await virtualDbContext.Set().AnyAsync()) + { + var ids = Enumerable.Range(1, 1000); + var userMods = new List(); + var userSalaries = new List(); + var beginTime = new DateTime(2020, 1, 1); + var endTime = new DateTime(2021, 12, 1); + foreach (var id in ids) + { + userMods.Add(new SysUserMod() + { + Id = id.ToString(), + Age = id, + Name = $"name_{id}", + AgeGroup = Math.Abs(id % 10) + }); + var tempTime = beginTime; + var i = 0; + while (tempTime <= endTime) + { + var dateOfMonth = $@"{tempTime:yyyyMM}"; + userSalaries.Add(new SysUserSalary() + { + Id = $@"{id}{dateOfMonth}", + UserId = id.ToString(), + DateOfMonth = int.Parse(dateOfMonth), + Salary = 700000 + id * 100 * i, + SalaryLong = 700000 + id * 100 * i, + SalaryDecimal = (700000 + id * 100 * i) / 100m, + SalaryDouble = (700000 + id * 100 * i) / 100d, + SalaryFloat = (700000 + id * 100 * i) / 100f + }); + tempTime = tempTime.AddMonths(1); + i++; + } + } + + await virtualDbContext.AddRangeAsync(userMods); + await virtualDbContext.AddRangeAsync(userSalaries); + + await virtualDbContext.SaveChangesAsync(); + } + } + } + } +} \ No newline at end of file diff --git a/test/ShardingCore.Test50_3x/DefaultDbContext.cs b/test/ShardingCore.Test50_3x/DefaultDbContext.cs index df5dc99b..0dab5e93 100644 --- a/test/ShardingCore.Test50_3x/DefaultDbContext.cs +++ b/test/ShardingCore.Test50_3x/DefaultDbContext.cs @@ -1,5 +1,5 @@ using Microsoft.EntityFrameworkCore; -using ShardingCore.Core.VirtualRoutes.RouteTails.Abstractions; +using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions; using ShardingCore.Sharding.Abstractions; using ShardingCore.Test50_3x.Domain.Maps; diff --git a/test/ShardingCore.Test50_3x/ShardingDefaultDbContext.cs b/test/ShardingCore.Test50_3x/ShardingDefaultDbContext.cs index ba574903..23a8bc7b 100644 --- a/test/ShardingCore.Test50_3x/ShardingDefaultDbContext.cs +++ b/test/ShardingCore.Test50_3x/ShardingDefaultDbContext.cs @@ -25,6 +25,5 @@ namespace ShardingCore.Test50_3x modelBuilder.ApplyConfiguration(new SysUserSalaryMap()); } - public override Type ShardingDbContextType => this.GetType(); } } diff --git a/test/ShardingCore.Test50_3x/Startup.cs b/test/ShardingCore.Test50_3x/Startup.cs index 53d8f78f..617d1b82 100644 --- a/test/ShardingCore.Test50_3x/Startup.cs +++ b/test/ShardingCore.Test50_3x/Startup.cs @@ -41,16 +41,17 @@ namespace ShardingCore.Test50_3x // ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services) public void ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext) { - services.AddShardingDbContext(o => o.UseSqlServer(hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"]) - ,op => + services.AddShardingDbContext(o => + o.UseSqlServer(hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"])) + .Begin(true, true) + .AddShardingQuery((conStr, builder) => builder.UseSqlServer(conStr).UseLoggerFactory(efLogger)) + .AddShardingTransaction((connection, builder) => builder.UseSqlServer(connection).UseLoggerFactory(efLogger)) + .AddDefaultDataSource("ds0", hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"]) + .AddShardingTable(op => { - op.EnsureCreatedWithOutShardingTable = true; - op.CreateShardingTableOnStart = true; - op.UseShardingOptionsBuilder((connection, builder) => builder.UseSqlServer(connection).UseLoggerFactory(efLogger), - (conStr,builder)=> builder.UseSqlServer(conStr).UseLoggerFactory(efLogger)); op.AddShardingTableRoute(); op.AddShardingTableRoute(); - }); + }).End(); } // 可以添加要用到的方法参数,会自动从注册的服务中获取服务实例,类似于 asp.net core 里 Configure 方法