From c1e1042e1ac9fa04f6b74f7e82458040e6b659a1 Mon Sep 17 00:00:00 2001 From: xuejiaming <326308290@qq.com> Date: Fri, 29 Oct 2021 10:44:48 +0800 Subject: [PATCH] =?UTF-8?q?[#47]=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E5=88=86=E8=A1=A8=E5=88=86=E5=BA=93=E6=97=A0?= =?UTF-8?q?=E9=9C=80=E9=80=9A=E8=BF=87=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- samples/Sample.BulkConsole/Program.cs | 1 + .../DefaultDesignTimeDbContextFactory.cs | 1 + samples/Sample.Migrations/Startup.cs | 1 + samples/Sample.MySql/DIExtension.cs | 1 + samples/Sample.SqlServer/DIExtension.cs | 1 + .../Domain/Entities/SysUserMod.cs | 2 +- samples/Sample.SqlServer3x/Startup.cs | 1 + .../DIExtension.cs | 1 + .../DIExtension.cs | 1 + .../Bootstrapers/EntityMetadataInitializer.cs | 37 +- .../IShardingBootstrapper.cs | 5 +- .../ShardingBootstrapper.cs | 17 +- .../Core/EntityMetadatas/EntityMetadata.cs | 6 +- ....cs => EntityMetadataDataSourceBuilder.cs} | 0 .../EntityMetadatas/IEntityMetadataManager.cs | 34 + .../IEntityMetadataManager`1.cs | 27 - .../Abstractions/IDataSourceRouteAssert.cs | 2 +- .../Abstractions/ITableRouteAssert.cs | 2 +- .../Core/ShardingTableKeyAttribute.cs | 4 +- .../VirtualDatabase/ShardingEntityConfig.cs | 64 - .../VirtualDataSources/VirtualDataSource.cs | 4 +- .../VirtualTables/IVirtualTable.cs | 1 + ...actShardingFilterVirtualDataSourceRoute.cs | 2 +- ...tShardingOperatorVirtualDataSourceRoute.cs | 2 +- .../AbstractVirtualDataSourceRoute.cs | 6 +- .../IVirtualDataSourceRoute.cs | 7 +- .../ShardingDataSourceRouteConfig.cs | 6 +- ...AbstractShardingFilterVirtualTableRoute.cs | 2 +- ...stractShardingOperatorVirtualTableRoute.cs | 2 +- .../Abstractions/AbstractVirtualTableRoute.cs | 6 +- .../TableRoutes/IVirtualTableRoute.cs | 3 +- .../RoutingRuleEngine/TableRouteRuleEngine.cs | 2 +- .../TableRoutes/ShardingTableRouteConfig.cs | 6 +- src/ShardingCore/DIExtension.cs | 4 + .../Extensions/DbContextExtension.cs | 2 +- .../ShardingDataSourceRouteExtension.cs | 12 +- .../Extensions/ShardingTableRouteExtension.cs | 12 +- .../Extensions/VirtualDataBaseExtension.cs | 2 +- .../Extensions/VirtualDataSourceExtension.cs | 2 +- .../EntityMetadataHelper.cs | 6 +- src/ShardingCore/Jobs/Abstaractions/IJob.cs | 8 + .../Jobs/Abstaractions/IJobExecutor.cs | 16 + .../Jobs/Abstaractions/IJobFactory.cs | 16 + .../Jobs/Abstaractions/IJobManager.cs | 21 + .../Jobs/Abstaractions/IJobTrigger.cs | 15 + src/ShardingCore/Jobs/Cron/CronExpression.cs | 2093 +++++++++++++++++ src/ShardingCore/Jobs/Cron/ISet.cs | 47 + src/ShardingCore/Jobs/Cron/ISortedSet.cs | 35 + src/ShardingCore/Jobs/Cron/TreeSet.cs | 170 ++ src/ShardingCore/Jobs/DIExtension.cs | 26 + .../Jobs/Extensions/CommonExtension.cs | 28 + .../Jobs/Impls/Attributes/JobRunAttribute.cs | 59 + .../Jobs/Impls/DefaultJobFactory.cs | 36 + .../Jobs/Impls/InMemoryJobManager.cs | 45 + src/ShardingCore/Jobs/Impls/JobEntry.cs | 79 + .../Jobs/Impls/JobGlobalOptions.cs | 16 + src/ShardingCore/Jobs/JobRunnerService.cs | 175 ++ src/ShardingCore/Jobs/JobTypeParser.cs | 71 + src/ShardingCore/Jobs/UtcTime.cs | 25 + ...eRouteShardingDataSourceDiscoverVisitor.cs | 445 ++-- .../TableCreator/IShardingTableCreator.cs | 9 +- .../TableCreator/ShardingTableCreator.cs | 2 +- src/ShardingCore/Utils/ShardingUtil.cs | 32 +- ...hardingTimeKeyDateTimeVirtualTableRoute.cs | 2 +- ...actShardingTimeKeyLongVirtualTableRoute.cs | 2 +- ...ShardingDayKeyDateTimeVirtualTableRoute.cs | 61 +- ...mpleShardingDayKeyLongVirtualTableRoute.cs | 64 +- ...impleShardingModKeyIntVirtualTableRoute.cs | 2 +- ...leShardingModKeyStringVirtualTableRoute.cs | 9 +- ...ardingMonthKeyDateTimeVirtualTableRoute.cs | 64 +- ...leShardingMonthKeyLongVirtualTableRoute.cs | 64 +- ...hardingWeekKeyDateTimeVirtualTableRoute.cs | 58 +- ...pleShardingWeekKeyLongVirtualTableRoute.cs | 59 +- ...hardingYearKeyDateTimeVirtualTableRoute.cs | 59 +- ...pleShardingYearKeyLongVirtualTableRoute.cs | 60 +- .../Domain/Entities/SysUserMod.cs | 2 +- .../Domain/Entities/SysUserRange.cs | 2 +- test/ShardingCore.Test50.MySql/Startup.cs | 1 + .../Domain/Entities/SysUserMod.cs | 5 +- .../Shardings/SysUserModVirtualTableRoute.cs | 6 + test/ShardingCore.Test50/Startup.cs | 1 + .../Domain/Entities/SysUserMod.cs | 2 +- test/ShardingCore.Test50_2x/Startup.cs | 1 + .../Domain/Entities/SysUserMod.cs | 2 +- test/ShardingCore.Test50_3x/Startup.cs | 1 + 86 files changed, 3882 insertions(+), 413 deletions(-) rename src/ShardingCore/{ => Bootstrapers}/IShardingBootstrapper.cs (72%) rename src/ShardingCore/{ => Bootstrapers}/ShardingBootstrapper.cs (70%) rename src/ShardingCore/Core/EntityMetadatas/{IEntityMetadataDataSourceBuilder.cs => EntityMetadataDataSourceBuilder.cs} (100%) create mode 100644 src/ShardingCore/Core/EntityMetadatas/IEntityMetadataManager.cs delete mode 100644 src/ShardingCore/Core/VirtualDatabase/ShardingEntityConfig.cs rename src/ShardingCore/{Utils => Helpers}/EntityMetadataHelper.cs (95%) create mode 100644 src/ShardingCore/Jobs/Abstaractions/IJob.cs create mode 100644 src/ShardingCore/Jobs/Abstaractions/IJobExecutor.cs create mode 100644 src/ShardingCore/Jobs/Abstaractions/IJobFactory.cs create mode 100644 src/ShardingCore/Jobs/Abstaractions/IJobManager.cs create mode 100644 src/ShardingCore/Jobs/Abstaractions/IJobTrigger.cs create mode 100644 src/ShardingCore/Jobs/Cron/CronExpression.cs create mode 100644 src/ShardingCore/Jobs/Cron/ISet.cs create mode 100644 src/ShardingCore/Jobs/Cron/ISortedSet.cs create mode 100644 src/ShardingCore/Jobs/Cron/TreeSet.cs create mode 100644 src/ShardingCore/Jobs/DIExtension.cs create mode 100644 src/ShardingCore/Jobs/Extensions/CommonExtension.cs create mode 100644 src/ShardingCore/Jobs/Impls/Attributes/JobRunAttribute.cs create mode 100644 src/ShardingCore/Jobs/Impls/DefaultJobFactory.cs create mode 100644 src/ShardingCore/Jobs/Impls/InMemoryJobManager.cs create mode 100644 src/ShardingCore/Jobs/Impls/JobEntry.cs create mode 100644 src/ShardingCore/Jobs/Impls/JobGlobalOptions.cs create mode 100644 src/ShardingCore/Jobs/JobRunnerService.cs create mode 100644 src/ShardingCore/Jobs/JobTypeParser.cs create mode 100644 src/ShardingCore/Jobs/UtcTime.cs diff --git a/README.md b/README.md index 084846ae..ea10ad69 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ---
-

Gitee Star 助力dotnet 生态 Github Star

+

Github Source Code 助力dotnet 生态 Gitee Source Code

--- diff --git a/samples/Sample.BulkConsole/Program.cs b/samples/Sample.BulkConsole/Program.cs index df4b8751..84c1267b 100644 --- a/samples/Sample.BulkConsole/Program.cs +++ b/samples/Sample.BulkConsole/Program.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using ShardingCore.Bootstrapers; using ShardingCore.Extensions.ShardingPageExtensions; namespace Sample.BulkConsole diff --git a/samples/Sample.Migrations/DefaultDesignTimeDbContextFactory.cs b/samples/Sample.Migrations/DefaultDesignTimeDbContextFactory.cs index d1d4961d..d49b1154 100644 --- a/samples/Sample.Migrations/DefaultDesignTimeDbContextFactory.cs +++ b/samples/Sample.Migrations/DefaultDesignTimeDbContextFactory.cs @@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.Extensions.DependencyInjection; using Sample.Migrations.EFCores; using ShardingCore; +using ShardingCore.Bootstrapers; namespace Sample.Migrations { diff --git a/samples/Sample.Migrations/Startup.cs b/samples/Sample.Migrations/Startup.cs index 846d1ac8..b49e3ac0 100644 --- a/samples/Sample.Migrations/Startup.cs +++ b/samples/Sample.Migrations/Startup.cs @@ -13,6 +13,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Migrations; using Sample.Migrations.EFCores; using ShardingCore; +using ShardingCore.Bootstrapers; namespace Sample.Migrations { diff --git a/samples/Sample.MySql/DIExtension.cs b/samples/Sample.MySql/DIExtension.cs index fd664d54..c3cc4f7b 100644 --- a/samples/Sample.MySql/DIExtension.cs +++ b/samples/Sample.MySql/DIExtension.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Sample.MySql.DbContexts; using Sample.MySql.Domain.Entities; using ShardingCore; +using ShardingCore.Bootstrapers; using ShardingCore.DbContexts.VirtualDbContexts; using ShardingCore.Extensions; diff --git a/samples/Sample.SqlServer/DIExtension.cs b/samples/Sample.SqlServer/DIExtension.cs index 1d993c42..7c11cdac 100644 --- a/samples/Sample.SqlServer/DIExtension.cs +++ b/samples/Sample.SqlServer/DIExtension.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.DependencyInjection; using Sample.SqlServer.DbContexts; using Sample.SqlServer.Domain.Entities; using ShardingCore; +using ShardingCore.Bootstrapers; namespace Sample.SqlServer { diff --git a/samples/Sample.SqlServer/Domain/Entities/SysUserMod.cs b/samples/Sample.SqlServer/Domain/Entities/SysUserMod.cs index 3fb0f392..520762d7 100644 --- a/samples/Sample.SqlServer/Domain/Entities/SysUserMod.cs +++ b/samples/Sample.SqlServer/Domain/Entities/SysUserMod.cs @@ -17,7 +17,7 @@ namespace Sample.SqlServer.Domain.Entities /// /// 用户Id用于分表 /// - [ShardingTableKey(TailPrefix = "_")] + [ShardingTableKey(TableSeparator = "_")] public string Id { get; set; } /// /// 用户名称 diff --git a/samples/Sample.SqlServer3x/Startup.cs b/samples/Sample.SqlServer3x/Startup.cs index 5f0a9c4c..1f201dd1 100644 --- a/samples/Sample.SqlServer3x/Startup.cs +++ b/samples/Sample.SqlServer3x/Startup.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Sample.SqlServer3x.Domain.Entities; using ShardingCore; +using ShardingCore.Bootstrapers; namespace Sample.SqlServer3x { diff --git a/samples/Sample.SqlServerShardingDataSource/DIExtension.cs b/samples/Sample.SqlServerShardingDataSource/DIExtension.cs index 2aa3e6ad..18d45897 100644 --- a/samples/Sample.SqlServerShardingDataSource/DIExtension.cs +++ b/samples/Sample.SqlServerShardingDataSource/DIExtension.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Sample.SqlServerShardingDataSource.DbContexts; using Sample.SqlServerShardingDataSource.Domain.Entities; using ShardingCore; +using ShardingCore.Bootstrapers; namespace Sample.SqlServerShardingDataSource { diff --git a/samples/Samples.AutoByDate.SqlServer/DIExtension.cs b/samples/Samples.AutoByDate.SqlServer/DIExtension.cs index a2fc648d..b6302bc9 100644 --- a/samples/Samples.AutoByDate.SqlServer/DIExtension.cs +++ b/samples/Samples.AutoByDate.SqlServer/DIExtension.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using ShardingCore; +using ShardingCore.Bootstrapers; namespace Samples.AutoByDate.SqlServer { diff --git a/src/ShardingCore/Bootstrapers/EntityMetadataInitializer.cs b/src/ShardingCore/Bootstrapers/EntityMetadataInitializer.cs index e5981afe..25a6296f 100644 --- a/src/ShardingCore/Bootstrapers/EntityMetadataInitializer.cs +++ b/src/ShardingCore/Bootstrapers/EntityMetadataInitializer.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.Extensions.Logging; using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.EntityShardingMetadatas; using ShardingCore.Core.PhysicTables; using ShardingCore.Core.TrackerManagers; using ShardingCore.Core.VirtualDatabase.VirtualDataSources; @@ -15,6 +16,10 @@ using ShardingCore.Core.VirtualRoutes.DataSourceRoutes; using ShardingCore.Core.VirtualRoutes.TableRoutes; using ShardingCore.Core.VirtualTables; using ShardingCore.Extensions; +using ShardingCore.Helpers; +using ShardingCore.Jobs; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls; using ShardingCore.Sharding.Abstractions; using ShardingCore.TableCreator; using ShardingCore.Utils; @@ -60,7 +65,7 @@ namespace ShardingCore.Bootstrapers { var shardingEntityType = _entityType.ClrType; _trackerManager.AddDbContextModel(shardingEntityType); - var entityMetadata = new EntityMetadata(shardingEntityType, _virtualTableName,_entityType.FindPrimaryKey().Properties.Select(o=>o.PropertyInfo).ToList()); + var entityMetadata = new EntityMetadata(shardingEntityType, _virtualTableName,typeof(TShardingDbContext),_entityType.FindPrimaryKey().Properties.Select(o=>o.PropertyInfo).ToList()); _entityMetadataManager.AddEntityMetadata(entityMetadata); //设置标签 if (_shardingConfigOption.TryGetVirtualDataSourceRoute(out var virtualDataSourceRouteType)) @@ -74,8 +79,10 @@ namespace ShardingCore.Bootstrapers entityMetadataAutoBindInitializer.Initialize(entityMetadata); } //配置分库信息 - var entityMetadataDataSourceConfiguration = dataSourceRoute.CreateEntityMetadataDataSourceConfiguration(); - entityMetadataDataSourceConfiguration?.Configure(creatEntityMetadataDataSourceBuilder); + if(dataSourceRoute is IEntityMetadataDataSourceConfiguration entityMetadataDataSourceConfiguration) + { + entityMetadataDataSourceConfiguration.Configure(creatEntityMetadataDataSourceBuilder); + } _virtualDataSource.AddVirtualDataSourceRoute(dataSourceRoute); @@ -92,14 +99,32 @@ namespace ShardingCore.Bootstrapers entityMetadataAutoBindInitializer.Initialize(entityMetadata); } //配置分表信息 - var createEntityMetadataTableConfiguration = virtualTableRoute.CreateEntityMetadataTableConfiguration(); - createEntityMetadataTableConfiguration?.Configure(entityMetadataTableBuilder); + if (virtualTableRoute is IEntityMetadataTableConfiguration createEntityMetadataTableConfiguration) + { + createEntityMetadataTableConfiguration.Configure(entityMetadataTableBuilder); + } //创建虚拟表 var virtualTable = CreateVirtualTable(virtualTableRoute,entityMetadata); _virtualTableManager.AddVirtualTable(virtualTable); + //检测校验分表分库对象元数据 + entityMetadata.CheckMetadata(); + //创建表 CreateDataTable(_dataSourceName, virtualTable); + //添加任务 + if (virtualTableRoute is IJob routeJob&& routeJob.StartJob()) + { + var jobManager = ShardingContainer.GetService(); + var jobEntries = JobTypeParser.Parse(virtualTableRoute.GetType()); + jobEntries.ForEach(o => + { + o.JobName = $"{routeJob.JobName}:{o.JobName}"; + }); + foreach (var jobEntry in jobEntries) + { + jobManager.AddJob(jobEntry); + } + } } - } private void CreateDataTable(string dataSourceName, IVirtualTable virtualTable) diff --git a/src/ShardingCore/IShardingBootstrapper.cs b/src/ShardingCore/Bootstrapers/IShardingBootstrapper.cs similarity index 72% rename from src/ShardingCore/IShardingBootstrapper.cs rename to src/ShardingCore/Bootstrapers/IShardingBootstrapper.cs index b0196738..243fa21b 100644 --- a/src/ShardingCore/IShardingBootstrapper.cs +++ b/src/ShardingCore/Bootstrapers/IShardingBootstrapper.cs @@ -1,7 +1,4 @@ -using System; -using System.Threading.Tasks; - -namespace ShardingCore +namespace ShardingCore.Bootstrapers { /* * @Author: xjm diff --git a/src/ShardingCore/ShardingBootstrapper.cs b/src/ShardingCore/Bootstrapers/ShardingBootstrapper.cs similarity index 70% rename from src/ShardingCore/ShardingBootstrapper.cs rename to src/ShardingCore/Bootstrapers/ShardingBootstrapper.cs index 7b3ed723..7e3da1ec 100644 --- a/src/ShardingCore/ShardingBootstrapper.cs +++ b/src/ShardingCore/Bootstrapers/ShardingBootstrapper.cs @@ -1,10 +1,12 @@ -using ShardingCore.Extensions; using System; using System.Collections.Generic; -using ShardingCore.Bootstrapers; +using System.Threading.Tasks; +using ShardingCore.Extensions; +using ShardingCore.Jobs; +using ShardingCore.Jobs.Abstaractions; using ShardingCore.Sharding.MergeEngines.ParallelControl; -namespace ShardingCore +namespace ShardingCore.Bootstrapers { /* * @Author: xjm @@ -32,6 +34,15 @@ namespace ShardingCore var instance = (IShardingDbContextBootstrapper)Activator.CreateInstance(typeof(ShardingDbContextBootstrapper<>).GetGenericType0(shardingConfigOption.ShardingDbContextType), shardingConfigOption); instance.Init(); } + + var jobManager = ShardingContainer.GetService(); + if (jobManager != null && jobManager.HasAnyJob()) + { + Task.Factory.StartNew(async () => + { + await ShardingContainer.GetService().StartAsync(); + }, TaskCreationOptions.LongRunning); + } } } diff --git a/src/ShardingCore/Core/EntityMetadatas/EntityMetadata.cs b/src/ShardingCore/Core/EntityMetadatas/EntityMetadata.cs index fc0fe348..3e3fbb24 100644 --- a/src/ShardingCore/Core/EntityMetadatas/EntityMetadata.cs +++ b/src/ShardingCore/Core/EntityMetadatas/EntityMetadata.cs @@ -8,10 +8,11 @@ namespace ShardingCore.Core.EntityMetadatas { public class EntityMetadata { - public EntityMetadata(Type entityType, string virtualTableName, IReadOnlyList primaryKeyProperties) + public EntityMetadata(Type entityType, string virtualTableName,Type shardingDbContextType, IReadOnlyList primaryKeyProperties) { EntityType = entityType; VirtualTableName = virtualTableName; + ShardingDbContextType = shardingDbContextType; PrimaryKeyProperties = primaryKeyProperties; IsSingleKey= PrimaryKeyProperties.Count == 1; } @@ -23,6 +24,9 @@ namespace ShardingCore.Core.EntityMetadatas /// 分表的原表名 original table name in db exclude tail /// public string VirtualTableName { get; } + + public Type ShardingDbContextType { get; } + /// /// 主键 /// diff --git a/src/ShardingCore/Core/EntityMetadatas/IEntityMetadataDataSourceBuilder.cs b/src/ShardingCore/Core/EntityMetadatas/EntityMetadataDataSourceBuilder.cs similarity index 100% rename from src/ShardingCore/Core/EntityMetadatas/IEntityMetadataDataSourceBuilder.cs rename to src/ShardingCore/Core/EntityMetadatas/EntityMetadataDataSourceBuilder.cs diff --git a/src/ShardingCore/Core/EntityMetadatas/IEntityMetadataManager.cs b/src/ShardingCore/Core/EntityMetadatas/IEntityMetadataManager.cs new file mode 100644 index 00000000..59ae952e --- /dev/null +++ b/src/ShardingCore/Core/EntityMetadatas/IEntityMetadataManager.cs @@ -0,0 +1,34 @@ +using System; +using Microsoft.EntityFrameworkCore; +using ShardingCore.Sharding.Abstractions; + +namespace ShardingCore.Core.EntityMetadatas +{ + public interface IEntityMetadataManager + { + /// + /// 添加元数据 + /// + /// + /// + bool AddEntityMetadata(EntityMetadata entityMetadata); + /// + /// 是否分表 + /// + /// + /// + bool IsShardingTable(Type entityType); + /// + /// 是否分库 + /// + /// + /// + bool IsShardingDataSource(Type entityType); + /// + /// 尝试获取 + /// + /// + /// + EntityMetadata TryGet(Type entityType); + } +} diff --git a/src/ShardingCore/Core/EntityMetadatas/IEntityMetadataManager`1.cs b/src/ShardingCore/Core/EntityMetadatas/IEntityMetadataManager`1.cs index e9d11b9c..ef5d7a64 100644 --- a/src/ShardingCore/Core/EntityMetadatas/IEntityMetadataManager`1.cs +++ b/src/ShardingCore/Core/EntityMetadatas/IEntityMetadataManager`1.cs @@ -4,33 +4,6 @@ using ShardingCore.Sharding.Abstractions; namespace ShardingCore.Core.EntityMetadatas { - public interface IEntityMetadataManager - { - /// - /// 添加元数据 - /// - /// - /// - bool AddEntityMetadata(EntityMetadata entityMetadata); - /// - /// 是否分表 - /// - /// - /// - bool IsShardingTable(Type entityType); - /// - /// 是否分库 - /// - /// - /// - bool IsShardingDataSource(Type entityType); - /// - /// 尝试获取 - /// - /// - /// - EntityMetadata TryGet(Type entityType); - } /// /// 分片 对象元数据信息管理 /// diff --git a/src/ShardingCore/Core/QueryRouteManagers/Abstractions/IDataSourceRouteAssert.cs b/src/ShardingCore/Core/QueryRouteManagers/Abstractions/IDataSourceRouteAssert.cs index 96cebf09..c9619519 100644 --- a/src/ShardingCore/Core/QueryRouteManagers/Abstractions/IDataSourceRouteAssert.cs +++ b/src/ShardingCore/Core/QueryRouteManagers/Abstractions/IDataSourceRouteAssert.cs @@ -24,7 +24,7 @@ namespace ShardingCore.Core.QueryRouteManagers.Abstractions void Assert(List allDataSources, List resultDataSources); } - public interface IDataSourceRouteAssert : IDataSourceRouteAssert where T : class, IShardingDataSource + public interface IDataSourceRouteAssert : IDataSourceRouteAssert where T : class { } diff --git a/src/ShardingCore/Core/QueryRouteManagers/Abstractions/ITableRouteAssert.cs b/src/ShardingCore/Core/QueryRouteManagers/Abstractions/ITableRouteAssert.cs index 97d58882..7ed11213 100644 --- a/src/ShardingCore/Core/QueryRouteManagers/Abstractions/ITableRouteAssert.cs +++ b/src/ShardingCore/Core/QueryRouteManagers/Abstractions/ITableRouteAssert.cs @@ -18,7 +18,7 @@ namespace ShardingCore.Core.QueryRouteManagers.Abstractions void Assert(List allPhysicTables, List resultPhysicTables); } - public interface ITableRouteAssert : ITableRouteAssert where T : class,IShardingTable + public interface ITableRouteAssert : ITableRouteAssert where T : class { } diff --git a/src/ShardingCore/Core/ShardingTableKeyAttribute.cs b/src/ShardingCore/Core/ShardingTableKeyAttribute.cs index 7f6a4547..3caf8c8d 100644 --- a/src/ShardingCore/Core/ShardingTableKeyAttribute.cs +++ b/src/ShardingCore/Core/ShardingTableKeyAttribute.cs @@ -19,7 +19,7 @@ namespace ShardingCore.Core [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public class ShardingTableKeyAttribute : Attribute { - public const string DEFAULT_TAIL_PREFIX = "_"; + public const string DEFAULT_TABLE_SEPARATOR = "_"; /// /// 是否需要在启动的时候创建表 @@ -29,6 +29,6 @@ namespace ShardingCore.Core /// /// 分表尾巴前缀 /// - public string TailPrefix { get; set; } = DEFAULT_TAIL_PREFIX; + public string TableSeparator { get; set; } = DEFAULT_TABLE_SEPARATOR; } } \ No newline at end of file diff --git a/src/ShardingCore/Core/VirtualDatabase/ShardingEntityConfig.cs b/src/ShardingCore/Core/VirtualDatabase/ShardingEntityConfig.cs deleted file mode 100644 index 3baec6f7..00000000 --- a/src/ShardingCore/Core/VirtualDatabase/ShardingEntityConfig.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text; - -namespace ShardingCore.Core.VirtualDatabase -{ - /* - * @Author: xjm - * @Description: - * @Date: 2021/9/16 9:30:04 - * @Ver: 1.0 - * @Email: 326308290@qq.com - */ - public class ShardingEntityConfig - { - /// - /// 分表类型 sharding entity type - /// - public Type EntityType { get; set; } - /// - /// 是否多数据源 - /// - public bool IsMultiDataSourceMapping { get; set; } - /// - /// 是否分表 - /// - public bool IsMultiTableMapping { get; set; } - /// - /// 分库字段 - /// - public string ShardingDataSourceField { get; set; } - /// - /// 主键名称 - /// - public string SinglePrimaryKeyFieldName { get; set; } - - - /// - /// 启动时是否建表 auto create data source when start app - /// - public bool? AutoCreateDataSourceTable { get; set; } - - /// - /// 分表字段 sharding table field - /// - public string ShardingTableField { get; set; } - - /// - /// 分表的原表名 original table name in db exclude tail - /// - public string VirtualTableName { get; set; } - - /// - /// 启动时是否建表 auto create table when start app - /// - public bool? AutoCreateTable { get; set; } - - /// - /// 分表尾巴后缀 table sharding tail prefix - /// - public string TailPrefix { get; set; } = "_"; - } -} diff --git a/src/ShardingCore/Core/VirtualDatabase/VirtualDataSources/VirtualDataSource.cs b/src/ShardingCore/Core/VirtualDatabase/VirtualDataSources/VirtualDataSource.cs index 62745f80..04f678bf 100644 --- a/src/ShardingCore/Core/VirtualDatabase/VirtualDataSources/VirtualDataSource.cs +++ b/src/ShardingCore/Core/VirtualDatabase/VirtualDataSources/VirtualDataSource.cs @@ -68,7 +68,7 @@ namespace ShardingCore.Core.VirtualDatabase.VirtualDataSources { if(!_entityMetadataManager.IsShardingDataSource(entityType)) throw new InvalidOperationException( - $"entity type :[{entityType.FullName}] not impl [{nameof(IShardingDataSource)}]"); + $"entity type :[{entityType.FullName}] not configure sharding data source"); if (!_dataSourceVirtualRoutes.TryGetValue(entityType, out var dataSourceVirtualRoute)) throw new InvalidOperationException( @@ -111,7 +111,7 @@ namespace ShardingCore.Core.VirtualDatabase.VirtualDataSources public bool AddVirtualDataSourceRoute(IVirtualDataSourceRoute virtualDataSourceRoute) { if (!virtualDataSourceRoute.EntityMetadata.IsShardingDataSource()) - throw new InvalidOperationException($"{virtualDataSourceRoute.EntityMetadata.EntityType.FullName} should impl {nameof(IShardingDataSource)}"); + throw new InvalidOperationException($"{virtualDataSourceRoute.EntityMetadata.EntityType.FullName} should configure sharding data source"); return _dataSourceVirtualRoutes.TryAdd(virtualDataSourceRoute.EntityMetadata.EntityType, virtualDataSourceRoute); } diff --git a/src/ShardingCore/Core/VirtualDatabase/VirtualTables/IVirtualTable.cs b/src/ShardingCore/Core/VirtualDatabase/VirtualTables/IVirtualTable.cs index dd6bfc32..74c9aef2 100644 --- a/src/ShardingCore/Core/VirtualDatabase/VirtualTables/IVirtualTable.cs +++ b/src/ShardingCore/Core/VirtualDatabase/VirtualTables/IVirtualTable.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ShardingCore.Bootstrapers; using ShardingCore.Core.EntityMetadatas; using ShardingCore.Core.PhysicTables; using ShardingCore.Core.VirtualDatabase; diff --git a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractShardingFilterVirtualDataSourceRoute.cs b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractShardingFilterVirtualDataSourceRoute.cs index 039deb53..238f5343 100644 --- a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractShardingFilterVirtualDataSourceRoute.cs +++ b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractShardingFilterVirtualDataSourceRoute.cs @@ -22,7 +22,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions /// /// /// - public abstract class AbstractShardingFilterVirtualDataSourceRoute : AbstractVirtualDataSourceRoute where T : class, IShardingDataSource + public abstract class AbstractShardingFilterVirtualDataSourceRoute : AbstractVirtualDataSourceRoute where T : class { public ShardingRouteContext CurrentShardingRouteContext => diff --git a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractShardingOperatorVirtualDataSourceRoute.cs b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractShardingOperatorVirtualDataSourceRoute.cs index 09539349..93a6e3cb 100644 --- a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractShardingOperatorVirtualDataSourceRoute.cs +++ b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractShardingOperatorVirtualDataSourceRoute.cs @@ -21,7 +21,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions /// /// /// - public abstract class AbstractShardingOperatorVirtualDataSourceRoute : AbstractShardingFilterVirtualDataSourceRoute where T : class, IShardingDataSource + public abstract class AbstractShardingOperatorVirtualDataSourceRoute : AbstractShardingFilterVirtualDataSourceRoute where T : class { protected override List DoRouteWithPredicate(List allDataSourceNames, IQueryable queryable) { diff --git a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractVirtualDataSourceRoute.cs b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractVirtualDataSourceRoute.cs index 9ad2c059..4486f7c2 100644 --- a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractVirtualDataSourceRoute.cs +++ b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/Abstractions/AbstractVirtualDataSourceRoute.cs @@ -14,7 +14,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions * @Date: Friday, 18 December 2020 14:33:01 * @Email: 326308290@qq.com */ - public abstract class AbstractVirtualDataSourceRoute : IVirtualDataSourceRoute, IEntityMetadataAutoBindInitializer where T : class, IShardingDataSource + public abstract class AbstractVirtualDataSourceRoute : IVirtualDataSourceRoute, IEntityMetadataAutoBindInitializer where T : class { public EntityMetadata EntityMetadata { get; private set; } private readonly DoOnlyOnce _doOnlyOnce = new DoOnlyOnce(); @@ -76,5 +76,9 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions public abstract List GetAllDataSourceNames(); public abstract bool AddDataSourceName(string dataSourceName); + public void Configure(EntityMetadataDataSourceBuilder builder) + { + + } } } \ 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 34c5f797..f4f3c66f 100644 --- a/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/IVirtualDataSourceRoute.cs +++ b/src/ShardingCore/Core/VirtualRoutes/DataSourceRoutes/IVirtualDataSourceRoute.cs @@ -52,17 +52,12 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes } - public interface IVirtualDataSourceRoute : IVirtualDataSourceRoute where T : class + public interface IVirtualDataSourceRoute : IVirtualDataSourceRoute, IEntityMetadataDataSourceConfiguration where T : class { /// /// 返回null就是表示不开启分页配置 /// /// IPaginationConfiguration CreatePaginationConfiguration(); - /// - /// 创建分库配置 - /// - /// - IEntityMetadataDataSourceConfiguration CreateEntityMetadataDataSourceConfiguration(); } } \ No newline at end of file diff --git a/src/ShardingCore/Core/VirtualRoutes/ShardingDataSourceRouteConfig.cs b/src/ShardingCore/Core/VirtualRoutes/ShardingDataSourceRouteConfig.cs index 47d002e2..8951fb06 100644 --- a/src/ShardingCore/Core/VirtualRoutes/ShardingDataSourceRouteConfig.cs +++ b/src/ShardingCore/Core/VirtualRoutes/ShardingDataSourceRouteConfig.cs @@ -13,12 +13,12 @@ namespace ShardingCore.Core.VirtualRoutes { private readonly IQueryable _queryable; - private readonly IShardingDataSource _shardingDataSource; + private readonly object _shardingDataSource; private readonly object _shardingKeyValue; private readonly Expression _predicate; - public ShardingDataSourceRouteConfig(IQueryable queryable=null,IShardingDataSource shardingDataSource=null,object shardingKeyValue=null,Expression predicate=null) + public ShardingDataSourceRouteConfig(IQueryable queryable=null,object shardingDataSource=null,object shardingKeyValue=null,Expression predicate=null) { _queryable = queryable; _shardingDataSource = shardingDataSource; @@ -35,7 +35,7 @@ namespace ShardingCore.Core.VirtualRoutes return _shardingKeyValue; } - public IShardingDataSource GetShardingDataSource() + public object GetShardingDataSource() { return _shardingDataSource; } diff --git a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingFilterVirtualTableRoute.cs b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingFilterVirtualTableRoute.cs index e52df0a4..6352663c 100644 --- a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingFilterVirtualTableRoute.cs +++ b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingFilterVirtualTableRoute.cs @@ -24,7 +24,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions /// /// /// - public abstract class AbstractShardingFilterVirtualTableRoute : AbstractVirtualTableRoute where T : class, IShardingTable + public abstract class AbstractShardingFilterVirtualTableRoute : AbstractVirtualTableRoute where T : class { public ShardingRouteContext CurrentShardingRouteContext => ShardingContainer.GetService().Current; diff --git a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingOperatorVirtualTableRoute.cs b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingOperatorVirtualTableRoute.cs index ebed404d..3e76c0ce 100644 --- a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingOperatorVirtualTableRoute.cs +++ b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractShardingOperatorVirtualTableRoute.cs @@ -17,7 +17,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions * @Date: Saturday, 19 December 2020 19:55:24 * @Email: 326308290@qq.com */ - public abstract class AbstractShardingOperatorVirtualTableRoute : AbstractShardingFilterVirtualTableRoute where T : class, IShardingTable + public abstract class AbstractShardingOperatorVirtualTableRoute : AbstractShardingFilterVirtualTableRoute where T : class { protected override List DoRouteWithPredicate(List allPhysicTables, IQueryable queryable) { diff --git a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractVirtualTableRoute.cs b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractVirtualTableRoute.cs index 203614a6..a81d666e 100644 --- a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractVirtualTableRoute.cs +++ b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/Abstractions/AbstractVirtualTableRoute.cs @@ -18,7 +18,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions * @Date: Friday, 18 December 2020 14:33:01 * @Email: 326308290@qq.com */ - public abstract class AbstractVirtualTableRoute : IVirtualTableRoute, IEntityMetadataAutoBindInitializer where T : class, IShardingTable + public abstract class AbstractVirtualTableRoute : IVirtualTableRoute, IEntityMetadataAutoBindInitializer where T : class { private readonly DoOnlyOnce _doOnlyOnce = new DoOnlyOnce(); @@ -77,5 +77,9 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions /// public abstract List GetAllTails(); + public virtual void Configure(EntityMetadataTableBuilder builder) + { + + } } } \ No newline at end of file diff --git a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/IVirtualTableRoute.cs b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/IVirtualTableRoute.cs index 115a4ca8..e8b3e18c 100644 --- a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/IVirtualTableRoute.cs +++ b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/IVirtualTableRoute.cs @@ -46,7 +46,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes } - public interface IVirtualTableRoute : IVirtualTableRoute where T : class + public interface IVirtualTableRoute : IVirtualTableRoute, IEntityMetadataTableConfiguration where T : class { /// /// 返回null就是表示不开启分页配置 @@ -54,6 +54,5 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes /// IPaginationConfiguration CreatePaginationConfiguration(); - IEntityMetadataTableConfiguration CreateEntityMetadataTableConfiguration(); } } \ No newline at end of file diff --git a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/RoutingRuleEngine/TableRouteRuleEngine.cs b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/RoutingRuleEngine/TableRouteRuleEngine.cs index 18e5e98a..ce6389a6 100644 --- a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/RoutingRuleEngine/TableRouteRuleEngine.cs +++ b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/RoutingRuleEngine/TableRouteRuleEngine.cs @@ -44,7 +44,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.RoutingRuleEngine { var virtualTable = _virtualTableManager.GetVirtualTable(shardingEntity); - var physicTables = virtualTable.RouteTo(new ShardingTableRouteConfig(tableRouteRuleContext.Queryable)); + var physicTables = virtualTable.RouteTo(new ShardingTableRouteConfig(queryable: tableRouteRuleContext.Queryable)); if (!routeMaps.ContainsKey(virtualTable)) { routeMaps.Add(virtualTable, physicTables.ToHashSet()); diff --git a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/ShardingTableRouteConfig.cs b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/ShardingTableRouteConfig.cs index b549b708..c38ef38b 100644 --- a/src/ShardingCore/Core/VirtualRoutes/TableRoutes/ShardingTableRouteConfig.cs +++ b/src/ShardingCore/Core/VirtualRoutes/TableRoutes/ShardingTableRouteConfig.cs @@ -12,12 +12,12 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes public class ShardingTableRouteConfig { private readonly IQueryable _queryable; - private readonly IShardingTable _shardingTable; + private readonly object _shardingTable; private readonly object _shardingKeyValue; private readonly Expression _predicate; - public ShardingTableRouteConfig(IQueryable queryable=null,IShardingTable shardingTable=null,object shardingKeyValue=null,Expression predicate=null) + public ShardingTableRouteConfig(IQueryable queryable=null,object shardingTable=null,object shardingKeyValue=null,Expression predicate=null) { _queryable = queryable; _shardingTable = shardingTable; @@ -34,7 +34,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes return _shardingKeyValue; } - public IShardingTable GetShardingEntity() + public object GetShardingEntity() { return _shardingTable; } diff --git a/src/ShardingCore/DIExtension.cs b/src/ShardingCore/DIExtension.cs index 223294b4..625bcdde 100644 --- a/src/ShardingCore/DIExtension.cs +++ b/src/ShardingCore/DIExtension.cs @@ -26,8 +26,10 @@ using ShardingCore.TableCreator; using System; using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Storage; +using ShardingCore.Bootstrapers; using ShardingCore.Core.EntityMetadatas; using ShardingCore.EFCores.OptionsExtensions; +using ShardingCore.Jobs; namespace ShardingCore { @@ -219,6 +221,8 @@ namespace ShardingCore services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); + + services.AddShardingJob(); return services; } public static DbContextOptionsBuilder UseSharding(this DbContextOptionsBuilder optionsBuilder) where TShardingDbContext : DbContext, IShardingDbContext diff --git a/src/ShardingCore/Extensions/DbContextExtension.cs b/src/ShardingCore/Extensions/DbContextExtension.cs index cfcf3001..e1e44be0 100644 --- a/src/ShardingCore/Extensions/DbContextExtension.cs +++ b/src/ShardingCore/Extensions/DbContextExtension.cs @@ -81,7 +81,7 @@ namespace ShardingCore.Extensions } public static void RemoveDbContextRelationModelSaveOnlyThatIsNamedType(this DbContext dbContext) - where T : IShardingTable + where T:class { RemoveDbContextRelationModelSaveOnlyThatIsNamedType(dbContext, typeof(T)); } diff --git a/src/ShardingCore/Extensions/ShardingDataSourceRouteExtension.cs b/src/ShardingCore/Extensions/ShardingDataSourceRouteExtension.cs index f5a0d7a3..278b9a34 100644 --- a/src/ShardingCore/Extensions/ShardingDataSourceRouteExtension.cs +++ b/src/ShardingCore/Extensions/ShardingDataSourceRouteExtension.cs @@ -25,7 +25,7 @@ namespace ShardingCore.Extensions /// /// /// 任何一个dataSources被添加成功就返回成功 - public static bool TryCreateOrAddMustDataSource(this ShardingRouteContext shardingRouteContext, params string[] dataSources) where TEntity : class, IShardingDataSource + public static bool TryCreateOrAddMustDataSource(this ShardingRouteContext shardingRouteContext, params string[] dataSources) where TEntity : class { return TryCreateOrAddMustDataSource(shardingRouteContext, typeof(TEntity), dataSources); } @@ -60,7 +60,7 @@ namespace ShardingCore.Extensions /// /// /// 任何一个dataSources被添加成功就返回成功 - public static bool TryCreateOrAddHintDataSource(this ShardingRouteContext shardingRouteContext, params string[] dataSources) where TEntity : class, IShardingDataSource + public static bool TryCreateOrAddHintDataSource(this ShardingRouteContext shardingRouteContext, params string[] dataSources) where TEntity : class { return TryCreateOrAddHintDataSource(shardingRouteContext, typeof(TEntity), dataSources); } @@ -95,7 +95,7 @@ namespace ShardingCore.Extensions /// /// /// - public static bool TryCreateOrAddAssertDataSource(this ShardingRouteContext shardingRouteContext, params IDataSourceRouteAssert[] dataSources) where TEntity : class, IShardingDataSource + public static bool TryCreateOrAddAssertDataSource(this ShardingRouteContext shardingRouteContext, params IDataSourceRouteAssert[] dataSources) where TEntity : class { return TryCreateOrAddAssertDataSource(shardingRouteContext, typeof(TEntity), dataSources); } @@ -123,7 +123,7 @@ namespace ShardingCore.Extensions - public static bool TryGetMustDataSource(this ShardingRouteContext shardingRouteContext, out HashSet dataSources) where TEntity : class, IShardingDataSource + public static bool TryGetMustDataSource(this ShardingRouteContext shardingRouteContext, out HashSet dataSources) where TEntity : class { return TryGetMustDataSource(shardingRouteContext,typeof(TEntity),out dataSources); } @@ -143,7 +143,7 @@ namespace ShardingCore.Extensions dataSources = shardingRouteContext.MustDataSource[entityType]; return true; } - public static bool TryGetHintDataSource(this ShardingRouteContext shardingRouteContext, out HashSet dataSources) where TEntity : class,IShardingDataSource + public static bool TryGetHintDataSource(this ShardingRouteContext shardingRouteContext, out HashSet dataSources) where TEntity : class { return TryGetHintDataSource(shardingRouteContext,typeof(TEntity),out dataSources); } @@ -164,7 +164,7 @@ namespace ShardingCore.Extensions return true; } - public static bool TryGetAssertDataSource(this ShardingRouteContext shardingRouteContext, out ICollection dataSources)where TEntity : class,IShardingDataSource + public static bool TryGetAssertDataSource(this ShardingRouteContext shardingRouteContext, out ICollection dataSources)where TEntity : class { return TryGetAssertDataSource(shardingRouteContext,typeof(TEntity), out dataSources); } diff --git a/src/ShardingCore/Extensions/ShardingTableRouteExtension.cs b/src/ShardingCore/Extensions/ShardingTableRouteExtension.cs index d54cd14d..02c4c256 100644 --- a/src/ShardingCore/Extensions/ShardingTableRouteExtension.cs +++ b/src/ShardingCore/Extensions/ShardingTableRouteExtension.cs @@ -25,7 +25,7 @@ namespace ShardingCore.Extensions /// /// /// 任何一个tails被添加成功就返回成功 - public static bool TryCreateOrAddMustTail(this ShardingRouteContext shardingRouteContext, params string[] tails) where TEntity : class, IShardingTable + public static bool TryCreateOrAddMustTail(this ShardingRouteContext shardingRouteContext, params string[] tails) where TEntity : class { return TryCreateOrAddMustTail(shardingRouteContext, typeof(TEntity), tails); } @@ -60,7 +60,7 @@ namespace ShardingCore.Extensions /// /// /// 任何一个tails被添加成功就返回成功 - public static bool TryCreateOrAddHintTail(this ShardingRouteContext shardingRouteContext, params string[] tails) where TEntity : class, IShardingTable + public static bool TryCreateOrAddHintTail(this ShardingRouteContext shardingRouteContext, params string[] tails) where TEntity : class { return TryCreateOrAddHintTail(shardingRouteContext, typeof(TEntity), tails); } @@ -95,7 +95,7 @@ namespace ShardingCore.Extensions /// /// /// - public static bool TryCreateOrAddAssertTail(this ShardingRouteContext shardingRouteContext, params ITableRouteAssert[] tails) where TEntity : class, IShardingTable + public static bool TryCreateOrAddAssertTail(this ShardingRouteContext shardingRouteContext, params ITableRouteAssert[] tails) where TEntity : class { return TryCreateOrAddAssertTail(shardingRouteContext, typeof(TEntity), tails); } @@ -123,7 +123,7 @@ namespace ShardingCore.Extensions - public static bool TryGetMustTail(this ShardingRouteContext shardingRouteContext, out HashSet tail) where TEntity : class,IShardingTable + public static bool TryGetMustTail(this ShardingRouteContext shardingRouteContext, out HashSet tail) where TEntity : class { return TryGetMustTail(shardingRouteContext,typeof(TEntity),out tail); } @@ -143,7 +143,7 @@ namespace ShardingCore.Extensions tail = shardingRouteContext.MustTable[entityType]; return true; } - public static bool TryGetHintTail(this ShardingRouteContext shardingRouteContext, out HashSet tail) where TEntity : class,IShardingTable + public static bool TryGetHintTail(this ShardingRouteContext shardingRouteContext, out HashSet tail) where TEntity : class { return TryGetHintTail(shardingRouteContext,typeof(TEntity),out tail); } @@ -164,7 +164,7 @@ namespace ShardingCore.Extensions return true; } - public static bool TryGetAssertTail(this ShardingRouteContext shardingRouteContext, out ICollection tail)where TEntity : class,IShardingTable + public static bool TryGetAssertTail(this ShardingRouteContext shardingRouteContext, out ICollection tail)where TEntity : class { return TryGetAssertTail(shardingRouteContext,typeof(TEntity), out tail); } diff --git a/src/ShardingCore/Extensions/VirtualDataBaseExtension.cs b/src/ShardingCore/Extensions/VirtualDataBaseExtension.cs index 8c61e289..ce19d942 100644 --- a/src/ShardingCore/Extensions/VirtualDataBaseExtension.cs +++ b/src/ShardingCore/Extensions/VirtualDataBaseExtension.cs @@ -75,7 +75,7 @@ namespace ShardingCore.Extensions public static string GetTableTail(this IVirtualTableManager virtualTableManager, TEntity entity) where TEntity : class { - var physicTable = virtualTableManager.GetVirtualTable(entity.GetType()).RouteTo(new ShardingTableRouteConfig(null, entity as IShardingTable, null))[0]; + var physicTable = virtualTableManager.GetVirtualTable(entity.GetType()).RouteTo(new ShardingTableRouteConfig(shardingTable: entity))[0]; return physicTable.Tail; } public static string GetTableTail(this IVirtualTableManager virtualTableManager, diff --git a/src/ShardingCore/Extensions/VirtualDataSourceExtension.cs b/src/ShardingCore/Extensions/VirtualDataSourceExtension.cs index 00261a42..99e67740 100644 --- a/src/ShardingCore/Extensions/VirtualDataSourceExtension.cs +++ b/src/ShardingCore/Extensions/VirtualDataSourceExtension.cs @@ -23,7 +23,7 @@ namespace ShardingCore.Extensions { return virtualDataSource.RouteTo(entity.GetType(), - new ShardingDataSourceRouteConfig(shardingDataSource: entity as IShardingDataSource))[0]; + new ShardingDataSourceRouteConfig(shardingDataSource: entity))[0]; } public static List GetDataSourceNames(this IVirtualDataSource virtualDataSource, Expression> where) diff --git a/src/ShardingCore/Utils/EntityMetadataHelper.cs b/src/ShardingCore/Helpers/EntityMetadataHelper.cs similarity index 95% rename from src/ShardingCore/Utils/EntityMetadataHelper.cs rename to src/ShardingCore/Helpers/EntityMetadataHelper.cs index dba4a167..bbd39b1c 100644 --- a/src/ShardingCore/Utils/EntityMetadataHelper.cs +++ b/src/ShardingCore/Helpers/EntityMetadataHelper.cs @@ -1,12 +1,10 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text; using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; -namespace ShardingCore.Utils +namespace ShardingCore.Helpers { public class EntityMetadataHelper { @@ -37,7 +35,7 @@ namespace ShardingCore.Utils ? (bool?) null : (shardingKey.AutoCreateTableOnStart == ShardingKeyAutoCreateTableEnum.Create); builder.AutoCreateTable(autoCreateTable); - builder.TableSeparator(shardingKey.TailPrefix); + builder.TableSeparator(shardingKey.TableSeparator); shardingTableCount++; } } diff --git a/src/ShardingCore/Jobs/Abstaractions/IJob.cs b/src/ShardingCore/Jobs/Abstaractions/IJob.cs new file mode 100644 index 00000000..abc9fb7e --- /dev/null +++ b/src/ShardingCore/Jobs/Abstaractions/IJob.cs @@ -0,0 +1,8 @@ +namespace ShardingCore.Jobs.Abstaractions +{ + internal interface IJob + { + string JobName { get; } + bool StartJob(); + } +} diff --git a/src/ShardingCore/Jobs/Abstaractions/IJobExecutor.cs b/src/ShardingCore/Jobs/Abstaractions/IJobExecutor.cs new file mode 100644 index 00000000..919a273d --- /dev/null +++ b/src/ShardingCore/Jobs/Abstaractions/IJobExecutor.cs @@ -0,0 +1,16 @@ +namespace ShardingCore.Jobs.Abstaractions +{ + /* + * @Author: xjm + * @Description: + * @Date: Friday, 08 January 2021 22:19:57 + * @Email: 326308290@qq.com + */ + internal interface IJobExecutor + { + void Run(); + bool IsRunning(); + bool StartJob(); + void Complete(); + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Abstaractions/IJobFactory.cs b/src/ShardingCore/Jobs/Abstaractions/IJobFactory.cs new file mode 100644 index 00000000..c535ca8c --- /dev/null +++ b/src/ShardingCore/Jobs/Abstaractions/IJobFactory.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Jobs.Impls; + +namespace ShardingCore.Jobs.Abstaractions +{ + /* + * @Author: xjm + * @Description: + * @Date: Friday, 08 January 2021 08:22:41 + * @Email: 326308290@qq.com + */ + internal interface IJobFactory + { + object CreateJobInstance(IServiceScope scope,JobEntry jobEntry); + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Abstaractions/IJobManager.cs b/src/ShardingCore/Jobs/Abstaractions/IJobManager.cs new file mode 100644 index 00000000..f676cea7 --- /dev/null +++ b/src/ShardingCore/Jobs/Abstaractions/IJobManager.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using ShardingCore.Jobs.Impls; + +namespace ShardingCore.Jobs.Abstaractions +{ + /* + * @Author: xjm + * @Description: + * @Date: Wednesday, 06 January 2021 13:10:13 + * @Email: 326308290@qq.com + */ + internal interface IJobManager + { + void AddJob(JobEntry jobEntry); + bool HasAnyJob(); + + List GetNowRunJobs(); + DateTime? GetNextJobUtcTime(); + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Abstaractions/IJobTrigger.cs b/src/ShardingCore/Jobs/Abstaractions/IJobTrigger.cs new file mode 100644 index 00000000..16d1bf89 --- /dev/null +++ b/src/ShardingCore/Jobs/Abstaractions/IJobTrigger.cs @@ -0,0 +1,15 @@ +using System; + +namespace ShardingCore.Jobs.Abstaractions +{ +/* +* @Author: xjm +* @Description: +* @Date: Friday, 08 January 2021 22:18:30 +* @Email: 326308290@qq.com +*/ + public interface IJobTrigger + { + DateTime NextJobRunUtcTime(); + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Cron/CronExpression.cs b/src/ShardingCore/Jobs/Cron/CronExpression.cs new file mode 100644 index 00000000..f3699b0a --- /dev/null +++ b/src/ShardingCore/Jobs/Cron/CronExpression.cs @@ -0,0 +1,2093 @@ +/* +* Copyright 2004-2009 James House +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy +* of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +* +*/ + + +using System; +using System.Collections; +using System.Globalization; +using System.Runtime.Serialization; +using System.Text; + + +//using TimeZone = System.TimeZoneInfo; + + +namespace ShardingCore.Jobs.Cron +{ + /// + /// Provides a parser and evaluator for unix-like cron expressions. Cron + /// expressions provide the ability to specify complex time combinations such as + /// "At 8:00am every Monday through Friday" or "At 1:30am every + /// last Friday of the month". + /// + /// + ///

+ /// Cron expressions are comprised of 6 required fields and one optional field + /// separated by white space. The fields respectively are described as follows: + ///

+ /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///
Field Name Allowed Values Allowed Special Characters
Seconds 0-59 , - /// /
Minutes 0-59 , - /// /
Hours 0-23 , - /// /
Day-of-month 1-31 , - /// ? / L W C
Month 1-12 or JAN-DEC , - /// /
Day-of-Week 1-7 or SUN-SAT , - /// ? / L #
Year (Optional) empty, 1970-2099 , - /// /
+ ///

+ /// The '*' character is used to specify all values. For example, "*" + /// in the minute field means "every minute". + ///

+ ///

+ /// The '?' character is allowed for the day-of-month and day-of-week fields. It + /// is used to specify 'no specific value'. This is useful when you need to + /// specify something in one of the two fields, but not the other. + ///

+ ///

+ /// The '-' character is used to specify ranges For example "10-12" in + /// the hour field means "the hours 10, 11 and 12". + ///

+ ///

+ /// The ',' character is used to specify additional values. For example + /// "MON,WED,FRI" in the day-of-week field means "the days Monday, + /// Wednesday, and Friday". + ///

+ ///

+ /// The '/' character is used to specify increments. For example "0/15" + /// in the seconds field means "the seconds 0, 15, 30, and 45". And + /// "5/15" in the seconds field means "the seconds 5, 20, 35, and + /// 50". Specifying '*' before the '/' is equivalent to specifying 0 is + /// the value to start with. Essentially, for each field in the expression, there + /// is a set of numbers that can be turned on or off. For seconds and minutes, + /// the numbers range from 0 to 59. For hours 0 to 23, for days of the month 0 to + /// 31, and for months 1 to 12. The "/" character simply helps you turn + /// on every "nth" value in the given set. Thus "7/6" in the + /// month field only turns on month "7", it does NOT mean every 6th + /// month, please note that subtlety. + ///

+ ///

+ /// The 'L' character is allowed for the day-of-month and day-of-week fields. + /// This character is short-hand for "last", but it has different + /// meaning in each of the two fields. For example, the value "L" in + /// the day-of-month field means "the last day of the month" - day 31 + /// for January, day 28 for February on non-leap years. If used in the + /// day-of-week field by itself, it simply means "7" or + /// "SAT". But if used in the day-of-week field after another value, it + /// means "the last xxx day of the month" - for example "6L" + /// means "the last friday of the month". When using the 'L' option, it + /// is important not to specify lists, or ranges of values, as you'll get + /// confusing results. + ///

+ ///

+ /// The 'W' character is allowed for the day-of-month field. This character + /// is used to specify the weekday (Monday-Friday) nearest the given day. As an + /// example, if you were to specify "15W" as the value for the + /// day-of-month field, the meaning is: "the nearest weekday to the 15th of + /// the month". So if the 15th is a Saturday, the trigger will fire on + /// Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the + /// 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th. + /// However if you specify "1W" as the value for day-of-month, and the + /// 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not + /// 'jump' over the boundary of a month's days. The 'W' character can only be + /// specified when the day-of-month is a single day, not a range or list of days. + ///

+ ///

+ /// The 'L' and 'W' characters can also be combined for the day-of-month + /// expression to yield 'LW', which translates to "last weekday of the + /// month". + ///

+ ///

+ /// The '#' character is allowed for the day-of-week field. This character is + /// used to specify "the nth" XXX day of the month. For example, the + /// value of "6#3" in the day-of-week field means the third Friday of + /// the month (day 6 = Friday and "#3" = the 3rd one in the month). + /// Other examples: "2#1" = the first Monday of the month and + /// "4#5" = the fifth Wednesday of the month. Note that if you specify + /// "#5" and there is not 5 of the given day-of-week in the month, then + /// no firing will occur that month. If the '#' character is used, there can + /// only be one expression in the day-of-week field ("3#1,6#3" is + /// not valid, since there are two expressions). + ///

+ ///

+ /// + ///

+ ///

+ /// The legal characters and the names of months and days of the week are not + /// case sensitive. + ///

+ ///

+ /// NOTES: + ///

    + ///
  • Support for specifying both a day-of-week and a day-of-month value is + /// not complete (you'll need to use the '?' character in one of these fields). + ///
  • + ///
  • Overflowing ranges is supported - that is, having a larger number on + /// the left hand side than the right. You might do 22-2 to catch 10 o'clock + /// at night until 2 o'clock in the morning, or you might have NOV-FEB. It is + /// very important to note that overuse of overflowing ranges creates ranges + /// that don't make sense and no effort has been made to determine which + /// interpretation CronExpression chooses. An example would be + /// "0 0 14-6 ? * FRI-MON".
  • + ///
+ ///

+ ///
+ /// Sharada Jambula + /// James House + /// Contributions from Mads Henderson + /// Refactoring from CronTrigger to CronExpression by Aaron Craven + [Serializable] + internal class CronExpression : ICloneable, IDeserializationCallback + { + /// + /// Field specification for second. + /// + protected const int Second = 0; + + /// + /// Field specification for minute. + /// + protected const int Minute = 1; + + /// + /// Field specification for hour. + /// + protected const int Hour = 2; + + /// + /// Field specification for day of month. + /// + protected const int DayOfMonth = 3; + + /// + /// Field specification for month. + /// + protected const int Month = 4; + + /// + /// Field specification for day of week. + /// + protected const int DayOfWeek = 5; + + /// + /// Field specification for year. + /// + protected const int Year = 6; + + /// + /// Field specification for all wildcard value '*'. + /// + protected const int AllSpecInt = 99; // '*' + + /// + /// Field specification for not specified value '?'. + /// + protected const int NoSpecInt = 98; // '?' + + /// + /// Field specification for wildcard '*'. + /// + protected const int AllSpec = AllSpecInt; + + /// + /// Field specification for no specification at all '?'. + /// + protected const int NoSpec = NoSpecInt; + + private static readonly Hashtable monthMap = new Hashtable(20); + private static readonly Hashtable dayMap = new Hashtable(60); + + private readonly string cronExpressionString = null; + + private TimeZone timeZone = null; + + /// + /// Seconds. + /// + [NonSerialized] + protected TreeSet seconds; + /// + /// minutes. + /// + [NonSerialized] + protected TreeSet minutes; + /// + /// Hours. + /// + [NonSerialized] + protected TreeSet hours; + /// + /// Days of month. + /// + [NonSerialized] + protected TreeSet daysOfMonth; + /// + /// Months. + /// + [NonSerialized] + protected TreeSet months; + /// + /// Days of week. + /// + [NonSerialized] + protected TreeSet daysOfWeek; + /// + /// Years. + /// + [NonSerialized] + protected TreeSet years; + + /// + /// Last day of week. + /// + [NonSerialized] + protected bool lastdayOfWeek = false; + /// + /// Nth day of week. + /// + [NonSerialized] + protected int nthdayOfWeek = 0; + /// + /// Last day of month. + /// + [NonSerialized] + protected bool lastdayOfMonth = false; + /// + /// Nearest weekday. + /// + [NonSerialized] + protected bool nearestWeekday = false; + /// + /// Calendar day of week. + /// + [NonSerialized] + protected bool calendardayOfWeek = false; + /// + /// Calendar day of month. + /// + [NonSerialized] + protected bool calendardayOfMonth = false; + /// + /// Expression parsed. + /// + [NonSerialized] + protected bool expressionParsed = false; + + static CronExpression() + { + monthMap.Add("JAN", 0); + monthMap.Add("FEB", 1); + monthMap.Add("MAR", 2); + monthMap.Add("APR", 3); + monthMap.Add("MAY", 4); + monthMap.Add("JUN", 5); + monthMap.Add("JUL", 6); + monthMap.Add("AUG", 7); + monthMap.Add("SEP", 8); + monthMap.Add("OCT", 9); + monthMap.Add("NOV", 10); + monthMap.Add("DEC", 11); + + dayMap.Add("SUN", 1); + dayMap.Add("MON", 2); + dayMap.Add("TUE", 3); + dayMap.Add("WED", 4); + dayMap.Add("THU", 5); + dayMap.Add("FRI", 6); + dayMap.Add("SAT", 7); + } + + /// + /// Constructs a new based on the specified + /// parameter. + /// + /// + /// String representation of the cron expression the new object should represent + /// + /// + public CronExpression(string cronExpression) + { + if (cronExpression == null) + { + throw new ArgumentException("cronExpression cannot be null"); + } + + cronExpressionString = cronExpression.ToUpper(CultureInfo.InvariantCulture); + BuildExpression(cronExpression); + } + + /// + /// Indicates whether the given date satisfies the cron expression. + /// + /// + /// Note that milliseconds are ignored, so two Dates falling on different milliseconds + /// of the same second will always have the same result here. + /// + /// The date to evaluate. + /// a boolean indicating whether the given date satisfies the cron expression + public virtual bool IsSatisfiedBy(DateTime dateUtc) + { + DateTime test = + new DateTime(dateUtc.Year, dateUtc.Month, dateUtc.Day, dateUtc.Hour, dateUtc.Minute, dateUtc.Second).AddSeconds(-1); + + DateTime? timeAfter = GetTimeAfter(test); + + if (timeAfter.HasValue && timeAfter.Value.Equals(dateUtc)) + { + return true; + } + else + { + return false; + } + } + + /// + /// Returns the next date/time after the given date/time which + /// satisfies the cron expression. + /// + /// the date/time at which to begin the search for the next valid date/time + /// the next valid date/time + public virtual DateTime? GetNextValidTimeAfter(DateTime date) + { + return GetTimeAfter(date); + } + + /// + /// Returns the next date/time after the given date/time which does + /// not satisfy the expression. + /// + /// the date/time at which to begin the search for the next invalid date/time + /// the next valid date/time + public virtual DateTime? GetNextInvalidTimeAfter(DateTime date) + { + long difference = 1000; + + //move back to the nearest second so differences will be accurate + DateTime lastDate = + new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second).AddSeconds(-1); + + //TODO: IMPROVE THIS! The following is a BAD solution to this problem. Performance will be very bad here, depending on the cron expression. It is, however A solution. + + //keep getting the next included time until it's farther than one second + // apart. At that point, lastDate is the last valid fire time. We return + // the second immediately following it. + while (difference == 1000) + { + DateTime newDate = GetTimeAfter(lastDate).Value; + + difference = (long) (newDate - lastDate).TotalMilliseconds; + + if (difference == 1000) + { + lastDate = newDate; + } + } + + return lastDate.AddSeconds(1); + } + + /// + /// Sets or gets the time zone for which the of this + /// will be resolved. + /// + public virtual TimeZone TimeZone + { + set { timeZone = value; } + get + { + if (timeZone == null) + { + timeZone = TimeZone.CurrentTimeZone; + } + + return timeZone; + } + } + + /// + /// Returns the string representation of the + /// + /// The string representation of the + public override string ToString() + { + return cronExpressionString; + } + + /// + /// Indicates whether the specified cron expression can be parsed into a + /// valid cron expression + /// + /// the expression to evaluate + /// a boolean indicating whether the given expression is a valid cron + /// expression + public static bool IsValidExpression(string cronExpression) + { + try + { + new CronExpression(cronExpression); + } + catch (FormatException) + { + return false; + } + + return true; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Expression Parsing Functions + // + //////////////////////////////////////////////////////////////////////////// + + /// + /// Builds the expression. + /// + /// The expression. + protected void BuildExpression(string expression) + { + expressionParsed = true; + + try + { + if (seconds == null) + { + seconds = new TreeSet(); + } + if (minutes == null) + { + minutes = new TreeSet(); + } + if (hours == null) + { + hours = new TreeSet(); + } + if (daysOfMonth == null) + { + daysOfMonth = new TreeSet(); + } + if (months == null) + { + months = new TreeSet(); + } + if (daysOfWeek == null) + { + daysOfWeek = new TreeSet(); + } + if (years == null) + { + years = new TreeSet(); + } + + int exprOn = Second; + +#if NET_20 + string[] exprsTok = expression.Trim().Split(new char[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); +#else + string[] exprsTok = expression.Trim().Split(new char[] { ' ', '\t', '\r', '\n' }); +#endif + foreach (string exprTok in exprsTok) + { + string expr = exprTok.Trim(); + + if (expr.Length == 0) + { + continue; + } + if (exprOn > Year) + { + break; + } + + // throw an exception if L is used with other days of the month + if (exprOn == DayOfMonth && expr.IndexOf('L') != -1 && expr.Length > 1 && expr.IndexOf(",") >= 0) + { + throw new FormatException("Support for specifying 'L' and 'LW' with other days of the month is not implemented"); + } + // throw an exception if L is used with other days of the week + if (exprOn == DayOfWeek && expr.IndexOf('L') != -1 && expr.Length > 1 && expr.IndexOf(",") >= 0) + { + throw new FormatException("Support for specifying 'L' with other days of the week is not implemented"); + } + + string[] vTok = expr.Split(','); + foreach (string v in vTok) + { + StoreExpressionVals(0, v, exprOn); + } + + exprOn++; + } + + if (exprOn <= DayOfWeek) + { + throw new FormatException("Unexpected end of expression."); + } + + if (exprOn <= Year) + { + StoreExpressionVals(0, "*", Year); + } + + TreeSet dow = GetSet(DayOfWeek); + TreeSet dom = GetSet(DayOfMonth); + + // Copying the logic from the UnsupportedOperationException below + bool dayOfMSpec = !dom.Contains(NoSpec); + bool dayOfWSpec = !dow.Contains(NoSpec); + + if (dayOfMSpec && !dayOfWSpec) + { + // skip + } + else if (dayOfWSpec && !dayOfMSpec) + { + // skip + } + else + { + throw new FormatException("Support for specifying both a day-of-week AND a day-of-month parameter is not implemented."); + } + } + catch (FormatException) + { + throw; + } + catch (Exception e) + { + throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Illegal cron expression format ({0})", e)); + } + } + + /// + /// Stores the expression values. + /// + /// The position. + /// The string to traverse. + /// The type of value. + /// + protected virtual int StoreExpressionVals(int pos, string s, int type) + { + int incr = 0; + int i = SkipWhiteSpace(pos, s); + if (i >= s.Length) + { + return i; + } + char c = s[i]; + if ((c >= 'A') && (c <= 'Z') && (!s.Equals("L")) && (!s.Equals("LW"))) + { + String sub = s.Substring(i, 3); + int sval; + int eval = -1; + if (type == Month) + { + sval = GetMonthNumber(sub) + 1; + if (sval <= 0) + { + throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Invalid Month value: '{0}'", sub)); + } + if (s.Length > i + 3) + { + c = s[i + 3]; + if (c == '-') + { + i += 4; + sub = s.Substring(i, 3); + eval = GetMonthNumber(sub) + 1; + if (eval <= 0) + { + throw new FormatException( + string.Format(CultureInfo.InvariantCulture, "Invalid Month value: '{0}'", sub)); + } + } + } + } + else if (type == DayOfWeek) + { + sval = GetDayOfWeekNumber(sub); + if (sval < 0) + { + throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Invalid Day-of-Week value: '{0}'", sub)); + } + if (s.Length > i + 3) + { + c = s[i + 3]; + if (c == '-') + { + i += 4; + sub = s.Substring(i, 3); + eval = GetDayOfWeekNumber(sub); + if (eval < 0) + { + throw new FormatException( + string.Format(CultureInfo.InvariantCulture, "Invalid Day-of-Week value: '{0}'", sub)); + } + } + else if (c == '#') + { + try + { + i += 4; + nthdayOfWeek = Convert.ToInt32(s.Substring(i), CultureInfo.InvariantCulture); + if (nthdayOfWeek < 1 || nthdayOfWeek > 5) + { + throw new Exception(); + } + } + catch (Exception) + { + throw new FormatException( + "A numeric value between 1 and 5 must follow the '#' option"); + } + } + else if (c == 'L') + { + lastdayOfWeek = true; + i++; + } + } + } + else + { + throw new FormatException( + string.Format(CultureInfo.InvariantCulture, "Illegal characters for this position: '{0}'", sub)); + } + if (eval != -1) + { + incr = 1; + } + AddToSet(sval, eval, incr, type); + return (i + 3); + } + + if (c == '?') + { + i++; + if ((i + 1) < s.Length + && (s[i] != ' ' && s[i + 1] != '\t')) + { + throw new FormatException("Illegal character after '?': " + + s[i]); + } + if (type != DayOfWeek && type != DayOfMonth) + { + throw new FormatException( + "'?' can only be specified for Day-of-Month or Day-of-Week."); + } + if (type == DayOfWeek && !lastdayOfMonth) + { + int val = (int) daysOfMonth[daysOfMonth.Count - 1]; + if (val == NoSpecInt) + { + throw new FormatException( + "'?' can only be specified for Day-of-Month -OR- Day-of-Week."); + } + } + + AddToSet(NoSpecInt, -1, 0, type); + return i; + } + + if (c == '*' || c == '/') + { + if (c == '*' && (i + 1) >= s.Length) + { + AddToSet(AllSpecInt, -1, incr, type); + return i + 1; + } + else if (c == '/' + && ((i + 1) >= s.Length || s[i + 1] == ' ' || s[i + 1] == '\t')) + { + throw new FormatException("'/' must be followed by an integer."); + } + else if (c == '*') + { + i++; + } + c = s[i]; + if (c == '/') + { + // is an increment specified? + i++; + if (i >= s.Length) + { + throw new FormatException("Unexpected end of string."); + } + + incr = GetNumericValue(s, i); + + i++; + if (incr > 10) + { + i++; + } + if (incr > 59 && (type == Second || type == Minute)) + { + throw new FormatException( + string.Format(CultureInfo.InvariantCulture, "Increment > 60 : {0}", incr)); + } + else if (incr > 23 && (type == Hour)) + { + throw new FormatException( + string.Format(CultureInfo.InvariantCulture, "Increment > 24 : {0}", incr)); + } + else if (incr > 31 && (type == DayOfMonth)) + { + throw new FormatException( + string.Format(CultureInfo.InvariantCulture, "Increment > 31 : {0}", incr)); + } + else if (incr > 7 && (type == DayOfWeek)) + { + throw new FormatException( + string.Format(CultureInfo.InvariantCulture, "Increment > 7 : {0}", incr)); + } + else if (incr > 12 && (type == Month)) + { + throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Increment > 12 : {0}", incr)); + } + } + else + { + incr = 1; + } + + AddToSet(AllSpecInt, -1, incr, type); + return i; + } + else if (c == 'L') + { + i++; + if (type == DayOfMonth) + { + lastdayOfMonth = true; + } + if (type == DayOfWeek) + { + AddToSet(7, 7, 0, type); + } + if (type == DayOfMonth && s.Length > i) + { + c = s[i]; + if (c == 'W') + { + nearestWeekday = true; + i++; + } + } + return i; + } + else if (c >= '0' && c <= '9') + { + int val = Convert.ToInt32(c.ToString(), CultureInfo.InvariantCulture); + i++; + if (i >= s.Length) + { + AddToSet(val, -1, -1, type); + } + else + { + c = s[i]; + if (c >= '0' && c <= '9') + { + ValueSet vs = GetValue(val, s, i); + val = vs.theValue; + i = vs.pos; + } + i = CheckNext(i, s, val, type); + return i; + } + } + else + { + throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Unexpected character: {0}", c)); + } + + return i; + } + + /// + /// Checks the next value. + /// + /// The position. + /// The string to check. + /// The value. + /// The type to search. + /// + protected virtual int CheckNext(int pos, string s, int val, int type) + { + int end = -1; + int i = pos; + + if (i >= s.Length) + { + AddToSet(val, end, -1, type); + return i; + } + + char c = s[pos]; + + if (c == 'L') + { + if (type == DayOfWeek) + { + lastdayOfWeek = true; + } + else + { + throw new FormatException(string.Format(CultureInfo.InvariantCulture, "'L' option is not valid here. (pos={0})", i)); + } + TreeSet data = GetSet(type); + data.Add(val); + i++; + return i; + } + + if (c == 'W') + { + if (type == DayOfMonth) + { + nearestWeekday = true; + } + else + { + throw new FormatException(string.Format(CultureInfo.InvariantCulture, "'W' option is not valid here. (pos={0})", i)); + } + TreeSet data = GetSet(type); + data.Add(val); + i++; + return i; + } + + if (c == '#') + { + if (type != DayOfWeek) + { + throw new FormatException( + string.Format(CultureInfo.InvariantCulture, "'#' option is not valid here. (pos={0})", i)); + } + i++; + try + { + nthdayOfWeek = Convert.ToInt32(s.Substring(i), CultureInfo.InvariantCulture); + if (nthdayOfWeek < 1 || nthdayOfWeek > 5) + { + throw new Exception(); + } + } + catch (Exception) + { + throw new FormatException( + "A numeric value between 1 and 5 must follow the '#' option"); + } + + TreeSet data = GetSet(type); + data.Add(val); + i++; + return i; + } + + if (c == 'C') + { + if (type == DayOfWeek) + { + calendardayOfWeek = true; + } + else if (type == DayOfMonth) + { + calendardayOfMonth = true; + } + else + { + throw new FormatException(string.Format(CultureInfo.InvariantCulture, "'C' option is not valid here. (pos={0})", i)); + } + TreeSet data = GetSet(type); + data.Add(val); + i++; + return i; + } + + if (c == '-') + { + i++; + c = s[i]; + int v = Convert.ToInt32(c.ToString(), CultureInfo.InvariantCulture); + end = v; + i++; + if (i >= s.Length) + { + AddToSet(val, end, 1, type); + return i; + } + c = s[i]; + if (c >= '0' && c <= '9') + { + ValueSet vs = GetValue(v, s, i); + int v1 = vs.theValue; + end = v1; + i = vs.pos; + } + if (i < s.Length && ((c = s[i]) == '/')) + { + i++; + c = s[i]; + int v2 = Convert.ToInt32(c.ToString(), CultureInfo.InvariantCulture); + i++; + if (i >= s.Length) + { + AddToSet(val, end, v2, type); + return i; + } + c = s[i]; + if (c >= '0' && c <= '9') + { + ValueSet vs = GetValue(v2, s, i); + int v3 = vs.theValue; + AddToSet(val, end, v3, type); + i = vs.pos; + return i; + } + else + { + AddToSet(val, end, v2, type); + return i; + } + } + else + { + AddToSet(val, end, 1, type); + return i; + } + } + + if (c == '/') + { + i++; + c = s[i]; + int v2 = Convert.ToInt32(c.ToString(), CultureInfo.InvariantCulture); + i++; + if (i >= s.Length) + { + AddToSet(val, end, v2, type); + return i; + } + c = s[i]; + if (c >= '0' && c <= '9') + { + ValueSet vs = GetValue(v2, s, i); + int v3 = vs.theValue; + AddToSet(val, end, v3, type); + i = vs.pos; + return i; + } + else + { + throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Unexpected character '{0}' after '/'", c)); + } + } + + AddToSet(val, end, 0, type); + i++; + return i; + } + + /// + /// Gets the cron expression string. + /// + /// The cron expression string. + public string CronExpressionString + { + get { return cronExpressionString; } + } + + /// + /// Gets the expression summary. + /// + /// + public virtual string GetExpressionSummary() + { + StringBuilder buf = new StringBuilder(); + + buf.Append("seconds: "); + buf.Append(GetExpressionSetSummary(seconds)); + buf.Append("\n"); + buf.Append("minutes: "); + buf.Append(GetExpressionSetSummary(minutes)); + buf.Append("\n"); + buf.Append("hours: "); + buf.Append(GetExpressionSetSummary(hours)); + buf.Append("\n"); + buf.Append("daysOfMonth: "); + buf.Append(GetExpressionSetSummary(daysOfMonth)); + buf.Append("\n"); + buf.Append("months: "); + buf.Append(GetExpressionSetSummary(months)); + buf.Append("\n"); + buf.Append("daysOfWeek: "); + buf.Append(GetExpressionSetSummary(daysOfWeek)); + buf.Append("\n"); + buf.Append("lastdayOfWeek: "); + buf.Append(lastdayOfWeek); + buf.Append("\n"); + buf.Append("nearestWeekday: "); + buf.Append(nearestWeekday); + buf.Append("\n"); + buf.Append("NthDayOfWeek: "); + buf.Append(nthdayOfWeek); + buf.Append("\n"); + buf.Append("lastdayOfMonth: "); + buf.Append(lastdayOfMonth); + buf.Append("\n"); + buf.Append("calendardayOfWeek: "); + buf.Append(calendardayOfWeek); + buf.Append("\n"); + buf.Append("calendardayOfMonth: "); + buf.Append(calendardayOfMonth); + buf.Append("\n"); + buf.Append("years: "); + buf.Append(GetExpressionSetSummary(years)); + buf.Append("\n"); + + return buf.ToString(); + } + + /// + /// Gets the expression set summary. + /// + /// The data. + /// + protected virtual string GetExpressionSetSummary(ISet data) + { + if (data.Contains(NoSpec)) + { + return "?"; + } + if (data.Contains(AllSpec)) + { + return "*"; + } + + StringBuilder buf = new StringBuilder(); + + bool first = true; + foreach (int iVal in data) + { + string val = iVal.ToString(CultureInfo.InvariantCulture); + if (!first) + { + buf.Append(","); + } + buf.Append(val); + first = false; + } + + return buf.ToString(); + } + + /// + /// Skips the white space. + /// + /// The i. + /// The s. + /// + protected virtual int SkipWhiteSpace(int i, string s) + { + for (; i < s.Length && (s[i] == ' ' || s[i] == '\t'); i++) + { + ; + } + + return i; + } + + /// + /// Finds the next white space. + /// + /// The i. + /// The s. + /// + protected virtual int FindNextWhiteSpace(int i, string s) + { + for (; i < s.Length && (s[i] != ' ' || s[i] != '\t'); i++) + { + ; + } + + return i; + } + + /// + /// Adds to set. + /// + /// The val. + /// The end. + /// The incr. + /// The type. + protected virtual void AddToSet(int val, int end, int incr, int type) + { + TreeSet data = GetSet(type); + + if (type == Second || type == Minute) + { + if ((val < 0 || val > 59 || end > 59) && (val != AllSpecInt)) + { + throw new FormatException( + "Minute and Second values must be between 0 and 59"); + } + } + else if (type == Hour) + { + if ((val < 0 || val > 23 || end > 23) && (val != AllSpecInt)) + { + throw new FormatException( + "Hour values must be between 0 and 23"); + } + } + else if (type == DayOfMonth) + { + if ((val < 1 || val > 31 || end > 31) && (val != AllSpecInt) + && (val != NoSpecInt)) + { + throw new FormatException( + "Day of month values must be between 1 and 31"); + } + } + else if (type == Month) + { + if ((val < 1 || val > 12 || end > 12) && (val != AllSpecInt)) + { + throw new FormatException( + "Month values must be between 1 and 12"); + } + } + else if (type == DayOfWeek) + { + if ((val == 0 || val > 7 || end > 7) && (val != AllSpecInt) + && (val != NoSpecInt)) + { + throw new FormatException( + "Day-of-Week values must be between 1 and 7"); + } + } + + if ((incr == 0 || incr == -1) && val != AllSpecInt) + { + if (val != -1) + { + data.Add(val); + } + else + { + data.Add(NoSpec); + } + return; + } + + int startAt = val; + int stopAt = end; + + if (val == AllSpecInt && incr <= 0) + { + incr = 1; + data.Add(AllSpec); // put in a marker, but also fill values + } + + if (type == Second || type == Minute) + { + if (stopAt == -1) + { + stopAt = 59; + } + if (startAt == -1 || startAt == AllSpecInt) + { + startAt = 0; + } + } + else if (type == Hour) + { + if (stopAt == -1) + { + stopAt = 23; + } + if (startAt == -1 || startAt == AllSpecInt) + { + startAt = 0; + } + } + else if (type == DayOfMonth) + { + if (stopAt == -1) + { + stopAt = 31; + } + if (startAt == -1 || startAt == AllSpecInt) + { + startAt = 1; + } + } + else if (type == Month) + { + if (stopAt == -1) + { + stopAt = 12; + } + if (startAt == -1 || startAt == AllSpecInt) + { + startAt = 1; + } + } + else if (type == DayOfWeek) + { + if (stopAt == -1) + { + stopAt = 7; + } + if (startAt == -1 || startAt == AllSpecInt) + { + startAt = 1; + } + } + else if (type == Year) + { + if (stopAt == -1) + { + stopAt = 2099; + } + if (startAt == -1 || startAt == AllSpecInt) + { + startAt = 1970; + } + } + + // if the end of the range is before the start, then we need to overflow into + // the next day, month etc. This is done by adding the maximum amount for that + // type, and using modulus max to determine the value being added. + int max = -1; + if (stopAt < startAt) + { + switch (type) + { + case Second: max = 60; break; + case Minute: max = 60; break; + case Hour: max = 24; break; + case Month: max = 12; break; + case DayOfWeek: max = 7; break; + case DayOfMonth: max = 31; break; + case Year: throw new ArgumentException("Start year must be less than stop year"); + default: throw new ArgumentException("Unexpected type encountered"); + } + stopAt += max; + } + + for (int i = startAt; i <= stopAt; i += incr) + { + if (max == -1) + { + // ie: there's no max to overflow over + data.Add(i); + } + else + { + // take the modulus to get the real value + int i2 = i % max; + + // 1-indexed ranges should not include 0, and should include their max + if (i2 == 0 && (type == Month || type == DayOfWeek || type == DayOfMonth)) + { + i2 = max; + } + + data.Add(i2); + } + } + } + + /// + /// Gets the set of given type. + /// + /// The type of set to get. + /// + protected virtual TreeSet GetSet(int type) + { + switch (type) + { + case Second: + return seconds; + case Minute: + return minutes; + case Hour: + return hours; + case DayOfMonth: + return daysOfMonth; + case Month: + return months; + case DayOfWeek: + return daysOfWeek; + case Year: + return years; + default: + return null; + } + } + + /// + /// Gets the value. + /// + /// The v. + /// The s. + /// The i. + /// + protected virtual ValueSet GetValue(int v, string s, int i) + { + char c = s[i]; + string s1 = v.ToString(CultureInfo.InvariantCulture); + while (c >= '0' && c <= '9') + { + s1 += c; + i++; + if (i >= s.Length) + { + break; + } + c = s[i]; + } + ValueSet val = new ValueSet(); + if (i < s.Length) + { + val.pos = i; + } + else + { + val.pos = i + 1; + } + val.theValue = Convert.ToInt32(s1, CultureInfo.InvariantCulture); + return val; + } + + /// + /// Gets the numeric value from string. + /// + /// The string to parse from. + /// The i. + /// + protected virtual int GetNumericValue(string s, int i) + { + int endOfVal = FindNextWhiteSpace(i, s); + string val = s.Substring(i, endOfVal - i); + return Convert.ToInt32(val, CultureInfo.InvariantCulture); + } + + /// + /// Gets the month number. + /// + /// The string to map with. + /// + protected virtual int GetMonthNumber(string s) + { + if (monthMap.ContainsKey(s)) + { + return (int) monthMap[s]; + } + else + { + return -1; + } + } + + /// + /// Gets the day of week number. + /// + /// The s. + /// + protected virtual int GetDayOfWeekNumber(string s) + { + if (dayMap.ContainsKey(s)) + { + return (int) dayMap[s]; + } + else + { + return -1; + } + } + + /// + /// Gets the time from given time parts. + /// + /// The seconds. + /// The minutes. + /// The hours. + /// The day of month. + /// The month. + /// + protected virtual DateTime? GetTime(int sc, int mn, int hr, int dayofmn, int mon) + { + try + { + if (sc == -1) + { + sc = 0; + } + if (mn == -1) + { + mn = 0; + } + if (hr == -1) + { + hr = 0; + } + if (dayofmn == -1) + { + dayofmn = 0; + } + if (mon == -1) + { + mon = 0; + } + return new DateTime(DateTime.UtcNow.Year, mon, dayofmn, hr, mn, sc); + } + catch (Exception) + { + return null; + } + } + + /// + /// Gets the next fire time after the given time. + /// + /// The UTC time to start searching from. + /// + public virtual DateTime? GetTimeAfter(DateTime afterTimeUtc) + { + // move ahead one second, since we're computing the time *after/// the + // given time + afterTimeUtc = afterTimeUtc.AddSeconds(1); + + // CronTrigger does not deal with milliseconds + DateTime d = CreateDateTimeWithoutMillis(afterTimeUtc); + + // change to specified time zone + + d = TimeZone.ToLocalTime(d); + + + bool gotOne = false; + // loop until we've computed the next time, or we've past the endTime + while (!gotOne) + { + ISortedSet st; + int t; + int sec = d.Second; + + // get second................................................. + st = seconds.TailSet(sec); + if (st != null && st.Count != 0) + { + sec = (int) st.First(); + } + else + { + sec = ((int) seconds.First()); + d = d.AddMinutes(1); + } + d = new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, sec, d.Millisecond); + + int min = d.Minute; + int hr = d.Hour; + t = -1; + + // get minute................................................. + st = minutes.TailSet(min); + if (st != null && st.Count != 0) + { + t = min; + min = ((int) st.First()); + } + else + { + min = (int) minutes.First(); + hr++; + } + if (min != t) + { + d = new DateTime(d.Year, d.Month, d.Day, d.Hour, min, 0, d.Millisecond); + d = SetCalendarHour(d, hr); + continue; + } + d = new DateTime(d.Year, d.Month, d.Day, d.Hour, min, d.Second, d.Millisecond); + + hr = d.Hour; + int day = d.Day; + t = -1; + + // get hour................................................... + st = hours.TailSet(hr); + if (st != null && st.Count != 0) + { + t = hr; + hr = (int) st.First(); + } + else + { + hr = (int) hours.First(); + day++; + } + if (hr != t) + { + int daysInMonth = DateTime.DaysInMonth(d.Year, d.Month); + if (day > daysInMonth) + { + d = new DateTime(d.Year, d.Month, daysInMonth, d.Hour, 0, 0, d.Millisecond).AddDays(day - daysInMonth); + } + else + { + d = new DateTime(d.Year, d.Month, day, d.Hour, 0, 0, d.Millisecond); + } + d = SetCalendarHour(d, hr); + continue; + } + d = new DateTime(d.Year, d.Month, d.Day, hr, d.Minute, d.Second, d.Millisecond); + + day = d.Day; + int mon = d.Month; + t = -1; + int tmon = mon; + + // get day................................................... + bool dayOfMSpec = !daysOfMonth.Contains(NoSpec); + bool dayOfWSpec = !daysOfWeek.Contains(NoSpec); + if (dayOfMSpec && !dayOfWSpec) + { + // get day by day of month rule + st = daysOfMonth.TailSet(day); + if (lastdayOfMonth) + { + if (!nearestWeekday) + { + t = day; + day = GetLastDayOfMonth(mon, d.Year); + } + else + { + t = day; + day = GetLastDayOfMonth(mon, d.Year); + + DateTime tcal = new DateTime(d.Year, mon, day, 0, 0, 0); + + int ldom = GetLastDayOfMonth(mon, d.Year); + DayOfWeek dow = tcal.DayOfWeek; + + if (dow == System.DayOfWeek.Saturday && day == 1) + { + day += 2; + } + else if (dow == System.DayOfWeek.Saturday) + { + day -= 1; + } + else if (dow == System.DayOfWeek.Sunday && day == ldom) + { + day -= 2; + } + else if (dow == System.DayOfWeek.Sunday) + { + day += 1; + } + + DateTime nTime = new DateTime(tcal.Year, mon, day, hr, min, sec, d.Millisecond); + if (nTime.ToUniversalTime() < afterTimeUtc) + { + day = 1; + mon++; + } + } + } + else if (nearestWeekday) + { + t = day; + day = (int) daysOfMonth.First(); + + DateTime tcal = new DateTime(d.Year, mon, day, 0, 0, 0); + + int ldom = GetLastDayOfMonth(mon, d.Year); + DayOfWeek dow = tcal.DayOfWeek; + + if (dow == System.DayOfWeek.Saturday && day == 1) + { + day += 2; + } + else if (dow == System.DayOfWeek.Saturday) + { + day -= 1; + } + else if (dow == System.DayOfWeek.Sunday && day == ldom) + { + day -= 2; + } + else if (dow == System.DayOfWeek.Sunday) + { + day += 1; + } + + tcal = new DateTime(tcal.Year, mon, day, hr, min, sec); + if (tcal.ToUniversalTime() < afterTimeUtc) + { + day = ((int) daysOfMonth.First()); + mon++; + } + } + else if (st != null && st.Count != 0) + { + t = day; + day = (int) st.First(); + + // make sure we don't over-run a short month, such as february + int lastDay = GetLastDayOfMonth(mon, d.Year); + if (day > lastDay) + { + day = (int) daysOfMonth.First(); + mon++; + } + } + else + { + day = ((int) daysOfMonth.First()); + mon++; + } + + if (day != t || mon != tmon) + { + if (mon > 12) + { + d = new DateTime(d.Year, 12, day, 0, 0, 0).AddMonths(mon - 12); + } + else + { + // This is to avoid a bug when moving from a month + //with 30 or 31 days to a month with less. Causes an invalid datetime to be instantiated. + // ex. 0 29 0 30 1 ? 2009 with clock set to 1/30/2009 + int lDay = DateTime.DaysInMonth(d.Year, mon); + if (day <= lDay) + { + d = new DateTime(d.Year, mon, day, 0, 0, 0); + } + else + { + d = new DateTime(d.Year, mon, lDay, 0, 0, 0).AddDays(day - lDay); + } + } + continue; + } + } + else if (dayOfWSpec && !dayOfMSpec) + { + // get day by day of week rule + if (lastdayOfWeek) + { + // are we looking for the last XXX day of + // the month? + int dow = ((int) daysOfWeek.First()); // desired + // d-o-w + int cDow = ((int) d.DayOfWeek) + 1; // current d-o-w + int daysToAdd = 0; + if (cDow < dow) + { + daysToAdd = dow - cDow; + } + if (cDow > dow) + { + daysToAdd = dow + (7 - cDow); + } + + int lDay = GetLastDayOfMonth(mon, d.Year); + + if (day + daysToAdd > lDay) + { + // did we already miss the + // last one? + if (mon == 12) + { + //will we pass the end of the year? + d = new DateTime(d.Year, mon - 11, 1, 0, 0, 0).AddYears(1); + } + else + { + d = new DateTime(d.Year, mon + 1, 1, 0, 0, 0); + } + // we are promoting the month + continue; + } + + // find date of last occurance of this day in this month... + while ((day + daysToAdd + 7) <= lDay) + { + daysToAdd += 7; + } + + day += daysToAdd; + + if (daysToAdd > 0) + { + d = new DateTime(d.Year, mon, day, 0, 0, 0); + // we are not promoting the month + continue; + } + } + else if (nthdayOfWeek != 0) + { + // are we looking for the Nth XXX day in the month? + int dow = ((int) daysOfWeek.First()); // desired + // d-o-w + int cDow = ((int) d.DayOfWeek) + 1; // current d-o-w + int daysToAdd = 0; + if (cDow < dow) + { + daysToAdd = dow - cDow; + } + else if (cDow > dow) + { + daysToAdd = dow + (7 - cDow); + } + + bool dayShifted = false; + if (daysToAdd > 0) + { + dayShifted = true; + } + + day += daysToAdd; + int weekOfMonth = day/7; + if (day%7 > 0) + { + weekOfMonth++; + } + + daysToAdd = (nthdayOfWeek - weekOfMonth)*7; + day += daysToAdd; + if (daysToAdd < 0 || day > GetLastDayOfMonth(mon, d.Year)) + { + if (mon == 12) + { + d = new DateTime(d.Year, mon - 11, 1, 0, 0, 0).AddYears(1); + } + else + { + d = new DateTime(d.Year, mon + 1, 1, 0, 0, 0); + } + + // we are promoting the month + continue; + } + else if (daysToAdd > 0 || dayShifted) + { + d = new DateTime(d.Year, mon, day, 0, 0, 0); + // we are NOT promoting the month + continue; + } + } + else + { + int cDow = ((int) d.DayOfWeek) + 1; // current d-o-w + int dow = ((int) daysOfWeek.First()); // desired + // d-o-w + st = daysOfWeek.TailSet(cDow); + if (st != null && st.Count > 0) + { + dow = ((int) st.First()); + } + + int daysToAdd = 0; + if (cDow < dow) + { + daysToAdd = dow - cDow; + } + if (cDow > dow) + { + daysToAdd = dow + (7 - cDow); + } + + int lDay = GetLastDayOfMonth(mon, d.Year); + + if (day + daysToAdd > lDay) + { + // will we pass the end of the month? + + if (mon == 12) + { + //will we pass the end of the year? + d = new DateTime(d.Year, mon - 11, 1, 0, 0, 0).AddYears(1); + } + else + { + d = new DateTime(d.Year, mon + 1, 1, 0, 0, 0); + } + // we are promoting the month + continue; + } + else if (daysToAdd > 0) + { + // are we swithing days? + d = new DateTime(d.Year, mon, day + daysToAdd, 0, 0, 0); + continue; + } + } + } + else + { + // dayOfWSpec && !dayOfMSpec + throw new Exception( + "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented."); + } + + d = new DateTime(d.Year, d.Month, day, d.Hour, d.Minute, d.Second); + mon = d.Month; + int year = d.Year; + t = -1; + + // test for expressions that never generate a valid fire date, + // but keep looping... + if (year > 2099) + { + return null; + } + + // get month................................................... + st = months.TailSet((mon)); + if (st != null && st.Count != 0) + { + t = mon; + mon = ((int) st.First()); + } + else + { + mon = ((int) months.First()); + year++; + } + if (mon != t) + { + d = new DateTime(year, mon, 1, 0, 0, 0); + continue; + } + d = new DateTime(d.Year, mon, d.Day, d.Hour, d.Minute, d.Second); + year = d.Year; + t = -1; + + // get year................................................... + st = years.TailSet((year)); + if (st != null && st.Count != 0) + { + t = year; + year = ((int) st.First()); + } + else + { + return null; + } // ran out of years... + + if (year != t) + { + d = new DateTime(year, 1, 1, 0, 0, 0); + continue; + } + d = new DateTime(year, d.Month, d.Day, d.Hour, d.Minute, d.Second); + + gotOne = true; + } // while( !done ) + return TimeZone.ToUniversalTime(d); + } + + /// + /// Creates the date time without milliseconds. + /// + /// The time. + /// + protected static DateTime CreateDateTimeWithoutMillis(DateTime time) + { + return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second); + } + + + /// + /// Advance the calendar to the particular hour paying particular attention + /// to daylight saving problems. + /// + /// The date. + /// The hour. + /// + protected static DateTime SetCalendarHour(DateTime date, int hour) + { + // Java version of Quartz uses lenient calendar + // so hour 24 creates day increment and zeroes hour + int hourToSet = hour; + if (hourToSet == 24) + { + hourToSet = 0; + } + DateTime d = + new DateTime(date.Year, date.Month, date.Day, hourToSet, date.Minute, date.Second, date.Millisecond); + if (hour == 24) + { + // inrement day + d = d.AddDays(1); + } + return d; + } + + /// + /// Gets the time before. + /// + /// The end time. + /// + public virtual DateTime? GetTimeBefore(DateTime? endTime) + { + // TODO: implement + return null; + } + + /// + /// NOT YET IMPLEMENTED: Returns the final time that the + /// will match. + /// + /// + public virtual DateTime? GetFinalFireTime() + { + // TODO: implement QUARTZ-423 + return null; + } + + /// + /// Determines whether given year is a leap year. + /// + /// The year. + /// + /// true if the specified year is a leap year; otherwise, false. + /// + protected virtual bool IsLeapYear(int year) + { + return DateTime.IsLeapYear(year); + } + + /// + /// Gets the last day of month. + /// + /// The month num. + /// The year. + /// + protected virtual int GetLastDayOfMonth(int monthNum, int year) + { + return DateTime.DaysInMonth(year, monthNum); + } + + /// + /// Creates a new object that is a copy of the current instance. + /// + /// + /// A new object that is a copy of this instance. + /// + public object Clone() + { + CronExpression copy; + try + { + copy = new CronExpression(CronExpressionString); + copy.TimeZone = TimeZone; + } + catch (FormatException) + { + // never happens since the source is valid... + throw new Exception("Not Cloneable."); + } + return copy; + } + + public void OnDeserialization(object sender) + { + BuildExpression(cronExpressionString); + } + } + + /// + /// Helper class for cron expression handling. + /// + public class ValueSet + { + /// + /// The value. + /// + public int theValue; + + /// + /// The position. + /// + public int pos; + } +} diff --git a/src/ShardingCore/Jobs/Cron/ISet.cs b/src/ShardingCore/Jobs/Cron/ISet.cs new file mode 100644 index 00000000..b073d775 --- /dev/null +++ b/src/ShardingCore/Jobs/Cron/ISet.cs @@ -0,0 +1,47 @@ +/* +* Copyright 2004-2009 James House +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy +* of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +* +*/ + +using System.Collections; + +namespace ShardingCore.Jobs.Cron +{ + /// + /// Represents a collection ob objects that contains no duplicate elements. + /// + internal interface ISet : ICollection, IList + { + /// + /// Adds a new element to the Collection if it is not already present. + /// + /// The object to add to the collection. + /// Returns true if the object was added to the collection, otherwise false. + new bool Add(object obj); + + /// + /// Adds all the elements of the specified collection to the Set. + /// + /// Collection of objects to add. + /// true + bool AddAll(ICollection c); + + /// + /// Returns the first item in the set. + /// + /// First object. + object First(); + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Cron/ISortedSet.cs b/src/ShardingCore/Jobs/Cron/ISortedSet.cs new file mode 100644 index 00000000..9813c9e7 --- /dev/null +++ b/src/ShardingCore/Jobs/Cron/ISortedSet.cs @@ -0,0 +1,35 @@ +/* +* Copyright 2004-2009 James House +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy +* of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +* +*/ + + +using ShardingCore.Jobs.Cron; + +namespace ShardingCore.Jobs +{ + /// + /// A sorted set. + /// + internal interface ISortedSet : ISet + { + /// + /// Returns a portion of the list whose elements are greater than the limit object parameter. + /// + /// The start element of the portion to extract. + /// The portion of the collection whose elements are greater than the limit object parameter. + ISortedSet TailSet(object limit); + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Cron/TreeSet.cs b/src/ShardingCore/Jobs/Cron/TreeSet.cs new file mode 100644 index 00000000..1f712b0c --- /dev/null +++ b/src/ShardingCore/Jobs/Cron/TreeSet.cs @@ -0,0 +1,170 @@ +/* +* Copyright 2004-2009 James House +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy +* of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +* +*/ + +using System; +using System.Collections; + +namespace ShardingCore.Jobs.Cron +{ + /// + /// SupportClass for the TreeSet class. + /// + [Serializable] + internal class TreeSet : ArrayList, ISortedSet + { + private readonly IComparer comparator = Comparer.Default; + + /// + /// Initializes a new instance of the class. + /// + public TreeSet() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The whose elements are copied to the new list. + /// + /// is . + public TreeSet(ICollection c) + { + AddAll(c); + } + + /// + /// Initializes a new instance of the class. + /// + /// The c. + public TreeSet(IComparer c) + { + comparator = c; + } + + /// + /// Unmodifiables the tree set. + /// + /// The collection. + /// + public static TreeSet UnmodifiableTreeSet(ICollection collection) + { + ArrayList items = new ArrayList(collection); + items = ReadOnly(items); + return new TreeSet(items); + } + + /// + /// Gets the IComparator object used to sort this set. + /// + public IComparer Comparator + { + get { return comparator; } + } + + private bool AddWithoutSorting(object obj) + { + bool inserted; + if (!(inserted = Contains(obj))) + { + base.Add(obj); + } + return !inserted; + } + + /// + /// Adds a new element to the ArrayList if it is not already present and sorts the ArrayList. + /// + /// Element to insert to the ArrayList. + /// TRUE if the new element was inserted, FALSE otherwise. + public new bool Add(object obj) + { + bool inserted = AddWithoutSorting(obj); + Sort(comparator); + return inserted; + } + + /// + /// Adds all the elements of the specified collection that are not present to the list. + /// + /// Collection where the new elements will be added + /// Returns true if at least one element was added to the collection. + public bool AddAll(ICollection c) + { + IEnumerator e = new ArrayList(c).GetEnumerator(); + bool added = false; + while (e.MoveNext()) + { + if (AddWithoutSorting(e.Current)) + { + added = true; + } + } + Sort(comparator); + return added; + } + + /// + /// Returns the first item in the set. + /// + /// First object. + public object First() + { + return this[0]; + } + + /// + /// Determines whether an element is in the the current TreeSetSupport collection. The IComparer defined for + /// the current set will be used to make comparisons between the elements already inserted in the collection and + /// the item specified. + /// + /// The object to be locatet in the current collection. + /// true if item is found in the collection; otherwise, false. + public override bool Contains(object item) + { + IEnumerator tempEnumerator = GetEnumerator(); + while (tempEnumerator.MoveNext()) + { + if (comparator.Compare(tempEnumerator.Current, item) == 0) + { + return true; + } + } + return false; + } + + + /// + /// Returns a portion of the list whose elements are greater than the limit object parameter. + /// + /// The start element of the portion to extract. + /// The portion of the collection whose elements are greater than the limit object parameter. + public ISortedSet TailSet(object limit) + { + ISortedSet newList = new TreeSet(); + int i = 0; + while ((i < Count) && (comparator.Compare(this[i], limit) < 0)) + { + i++; + } + for (; i < Count; i++) + { + newList.Add(this[i]); + } + return newList; + } + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/DIExtension.cs b/src/ShardingCore/Jobs/DIExtension.cs new file mode 100644 index 00000000..11fc722e --- /dev/null +++ b/src/ShardingCore/Jobs/DIExtension.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls; + +namespace ShardingCore.Jobs +{ +/* +* @Author: xjm +* @Description: +* @Date: Thursday, 07 January 2021 13:34:52 +* @Email: 326308290@qq.com +*/ + internal static class DIExtension + { + public static IServiceCollection AddShardingJob(this IServiceCollection services, Action config=null) + { + var option = new JobGlobalOptions(); + config?.Invoke(option); + services.AddSingleton(sp => option).AddSingleton() + .AddSingleton() + .AddSingleton(); + return services; + } + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Extensions/CommonExtension.cs b/src/ShardingCore/Jobs/Extensions/CommonExtension.cs new file mode 100644 index 00000000..a5e12444 --- /dev/null +++ b/src/ShardingCore/Jobs/Extensions/CommonExtension.cs @@ -0,0 +1,28 @@ +using System; +using System.Reflection; +using System.Threading.Tasks; +using ShardingCore.Jobs.Abstaractions; + +namespace ShardingCore.Jobs.Extensions +{ +/* +* @Author: xjm +* @Description: +* @Date: Wednesday, 06 January 2021 13:08:55 +* @Email: 326308290@qq.com +*/ + internal static class CommonExtension + { + + public static bool IsJobType(this Type entityType) + { + if (entityType == null) + throw new ArgumentNullException(nameof(entityType)); + return typeof(IJob).IsAssignableFrom(entityType); + } + public static bool IsAsyncMethod(this MethodInfo method) + { + return (typeof(Task).IsAssignableFrom(method.ReturnType)); + } + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Impls/Attributes/JobRunAttribute.cs b/src/ShardingCore/Jobs/Impls/Attributes/JobRunAttribute.cs new file mode 100644 index 00000000..36aea2d7 --- /dev/null +++ b/src/ShardingCore/Jobs/Impls/Attributes/JobRunAttribute.cs @@ -0,0 +1,59 @@ +using System; + +namespace ShardingCore.Jobs.Impls.Attributes +{ +/* +* @Author: xjm +* @Description: +* @Date: Wednesday, 06 January 2021 13:04:57 +* @Email: 326308290@qq.com +*/ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + internal class JobRunAttribute:Attribute + { + /// + /// 任务名称 + /// + public string Name { get; set; } + + /// + /// 开始时间 + /// + public DateTime BeginUtcTime => GetBeginUtcTime(); + /// + /// 任务开始时间优先级最高,格式:yyyy/MM/dd HH:mm:ss、yyyy-MM-dd HH:mm:ss + /// + public string Begin { get; set; } + + private DateTime GetBeginUtcTime() + { + if (!string.IsNullOrWhiteSpace(Begin)) + { + var localTime = Convert.ToDateTime(Begin); + var utcOffset = TimeZoneInfo.Local.BaseUtcOffset.TotalMilliseconds; + return localTime.AddMilliseconds(-utcOffset); + } + else + { + return DateTime.UtcNow; + } + } + /// + /// 默认每分钟执行 + /// + public string Cron { get; set; } = "0 * * * * ? *"; + /// + /// 如果正在运行就跳过 + /// + public bool SkipIfRunning { get; set; } = true; + + /// + /// 是否从di容器中获取 + /// + public bool CreateFromServiceProvider { get; set; } = false; + /// + /// 是否马上执行在程序启动后 需要优先满足Begin条件 + /// + public bool RunOnceOnStart { get; set; } = false; + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Impls/DefaultJobFactory.cs b/src/ShardingCore/Jobs/Impls/DefaultJobFactory.cs new file mode 100644 index 00000000..e77bee18 --- /dev/null +++ b/src/ShardingCore/Jobs/Impls/DefaultJobFactory.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using ShardingCore.Jobs.Abstaractions; + +namespace ShardingCore.Jobs.Impls +{ + /* + * @Author: xjm + * @Description: + * @Date: Friday, 08 January 2021 08:25:24 + * @Email: 326308290@qq.com + */ + internal class DefaultJobFactory:IJobFactory + { + public object CreateJobInstance(IServiceScope scope,JobEntry jobEntry) + { + if (jobEntry.CreateFromServiceProvider) + { + return scope.ServiceProvider.GetService(jobEntry.JobClass); + } + var args = jobEntry.JobClass.GetConstructors() + .First() + .GetParameters() + .Select(x => + { + if (x.ParameterType == typeof(IServiceProvider)) + return scope.ServiceProvider; + else + return scope.ServiceProvider.GetService(x.ParameterType); + }) + .ToArray(); + return Activator.CreateInstance(jobEntry.JobClass, args); + } + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Impls/InMemoryJobManager.cs b/src/ShardingCore/Jobs/Impls/InMemoryJobManager.cs new file mode 100644 index 00000000..fe1a9f0a --- /dev/null +++ b/src/ShardingCore/Jobs/Impls/InMemoryJobManager.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ShardingCore.Jobs.Abstaractions; + +namespace ShardingCore.Jobs.Impls +{ + /* + * @Author: xjm + * @Description: + * @Date: Wednesday, 06 January 2021 13:11:38 + * @Email: 326308290@qq.com + */ + internal class InMemoryJobManager : IJobManager + { + private readonly List _jobs = new List(); + + + public void AddJob(JobEntry jobEntry) + { + if (_jobs.Any(job => job.JobName == jobEntry.JobName)) + throw new ArgumentException($"发现重复的任务名称:{jobEntry.JobName},请确认"); + _jobs.Add(jobEntry); + } + + public bool HasAnyJob() + { + return _jobs.Any(); + } + + public List GetNowRunJobs() + { + var now = DateTime.UtcNow; + return _jobs.Where(o => o.NextUtcTime.HasValue && o.NextUtcTime.Value <= now&&!o.Running).ToList(); + } + + public DateTime? GetNextJobUtcTime() + { + var waitRunJobs = _jobs.Where(o => o.NextUtcTime.HasValue&&!o.Running).ToArray(); + if (!waitRunJobs.Any()) + return null; + return waitRunJobs.Min(o => o.NextUtcTime.Value); + } + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Impls/JobEntry.cs b/src/ShardingCore/Jobs/Impls/JobEntry.cs new file mode 100644 index 00000000..2fa61bd6 --- /dev/null +++ b/src/ShardingCore/Jobs/Impls/JobEntry.cs @@ -0,0 +1,79 @@ +using System; +using System.Reflection; +using System.Threading; + +namespace ShardingCore.Jobs.Impls +{ +/* +* @Author: xjm +* @Description: +* @Date: Wednesday, 06 January 2021 13:13:23 +* @Email: 326308290@qq.com +*/ + internal class JobEntry + { + /// + /// 保证多线程只有一个清理操作 + /// + private const int running = 1; + + /// + /// 为运行 + /// + private const int unrunning = 0; + + /// + /// 任务名称 + /// + public string JobName { get; set; } + + public Type JobClass { get; set; } + public MethodInfo JobMethod { get; set; } + + /// + /// 开始时间 + /// + public DateTime BeginUtcTime { get; set; } + + /// + /// 表达式 + /// + public string Cron { get; set; } + + /// + /// 是否跳过如果正在运行 + /// + public bool SkipIfRunning { get; set; } + /// + /// 是否从di容器中获取 + /// + + public bool CreateFromServiceProvider { get; set; } + + /// + /// 下次运行时间 + /// + public DateTime? NextUtcTime { get; set; } + + /// + /// 是否正在运行 + /// + public bool Running => runStatus == running; + + + private int runStatus = unrunning; + + public bool StartRun() + { + if (SkipIfRunning) + return Interlocked.CompareExchange(ref runStatus, running, unrunning) == unrunning; + return true; + } + + public void CompleteRun() + { + if (SkipIfRunning) + runStatus = unrunning; + } + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/Impls/JobGlobalOptions.cs b/src/ShardingCore/Jobs/Impls/JobGlobalOptions.cs new file mode 100644 index 00000000..6eaa70d1 --- /dev/null +++ b/src/ShardingCore/Jobs/Impls/JobGlobalOptions.cs @@ -0,0 +1,16 @@ +namespace ShardingCore.Jobs.Impls +{ + /* + * @Author: xjm + * @Description: + * @Date: Friday, 08 January 2021 08:16:08 + * @Email: 326308290@qq.com + */ + internal class JobGlobalOptions + { + /// + /// 延迟启动时间 + /// + public int DelaySecondsOnStart { get; set; } = 10; + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/JobRunnerService.cs b/src/ShardingCore/Jobs/JobRunnerService.cs new file mode 100644 index 00000000..6137a3e0 --- /dev/null +++ b/src/ShardingCore/Jobs/JobRunnerService.cs @@ -0,0 +1,175 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Cron; +using ShardingCore.Jobs.Extensions; +using ShardingCore.Jobs.Impls; + +namespace ShardingCore.Jobs +{ +/* +* @Author: xjm +* @Description: +* @Date: Wednesday, 06 January 2021 13:00:11 +* @Email: 326308290@qq.com +*/ + internal class JobRunnerService + { + private readonly IServiceProvider _serviceProvider; + private readonly JobGlobalOptions _jobGlobalOptions; + private readonly IJobManager _jobManager; + private readonly ILogger _logger; + private readonly IJobFactory _jobFactory; + private readonly CancellationTokenSource _cts = new CancellationTokenSource(); + private const long DEFAULT_MILLIS = 1000L; + + /// + /// 最大休眠时间30秒 + /// + private const long MAX_DELAY_MILLIS = 30000L; + + public JobRunnerService(IServiceProvider serviceProvider,JobGlobalOptions jobGlobalOptions, IJobManager jobManager, ILogger logger, IJobFactory jobFactory) + { + _serviceProvider = serviceProvider; + _jobGlobalOptions = jobGlobalOptions; + _jobManager = jobManager; + _logger = logger; + _jobFactory = jobFactory; + } + + //private void Init() + //{ + // var assemblies = AssemblyHelper.CurrentDomain.GetAssemblies(); + // foreach (var x in assemblies) + // { + // // 查找接口为Job的类 + // var types = x.DefinedTypes.Where(y => y.IsJobType()).ToList(); + // foreach (var y in types) + // { + // var jobs = JobTypeParser.Parse(y.AsType()); + // foreach (var job in jobs) + // { + // _jobManager.AddJob(job); + // } + // } + // } + //} + + public async Task StartAsync() + { + if (_jobGlobalOptions.DelaySecondsOnStart > 0) + await Task.Delay(TimeSpan.FromSeconds(_jobGlobalOptions.DelaySecondsOnStart),_cts.Token); + while (!_cts.Token.IsCancellationRequested) + { + var delayMs = 0L; + try + { + delayMs = LoopJobAndGetWaitMillis(); + } + catch (Exception e) + { + _logger.LogError($"job runner service exception : {e}"); + await Task.Delay((int) DEFAULT_MILLIS, _cts.Token); + } + + if (delayMs > 0) + await Task.Delay((int) Math.Min(MAX_DELAY_MILLIS, delayMs), _cts.Token); //最大休息为MAX_DELAY_MILLIS + } + } + + public Task StopAsync() + { + _cts.Cancel(); + return Task.CompletedTask; + } + + /// + /// + /// + /// next utc time that job when restart + private long LoopJobAndGetWaitMillis() + { + var beginTime = UtcTime.CurrentTimeMillis(); + var costTime = 0L; + var runJobs = _jobManager.GetNowRunJobs(); + if (!runJobs.Any()) + { + var minJobUtcTime = _jobManager.GetNextJobUtcTime(); + if (!minJobUtcTime.HasValue) + { + //return wait one second + costTime = UtcTime.CurrentTimeMillis() - beginTime; + if (DEFAULT_MILLIS < costTime) + return 0L; + return DEFAULT_MILLIS - costTime; + } + else + { + //return next job run time + return UtcTime.InputUtcTimeMillis(minJobUtcTime.Value) - beginTime; + } + } + + foreach (var job in runJobs) + { + DoJob(job); + } + + costTime = UtcTime.CurrentTimeMillis() - beginTime; + if (costTime > DEFAULT_MILLIS) + { + return 0L; + } + + return DEFAULT_MILLIS - costTime; + } + + private void DoJob(JobEntry jobEntry) + { + if (jobEntry.StartRun()) + { + Task.Factory.StartNew(async () => + { + try + { + using (var scope = _serviceProvider.CreateScope()) + { + var job = _jobFactory.CreateJobInstance(scope, jobEntry); + if (job == null) + _logger.LogWarning($"### job [{jobEntry.JobName}] cant created, create method :{(jobEntry.CreateFromServiceProvider ? "from service provider" : "activator")}"); + var method = jobEntry.JobMethod; + var @params = method.GetParameters().Select(x => scope.ServiceProvider.GetService(x.ParameterType)).ToArray(); + + _logger.LogInformation($"### job [{jobEntry.JobName}] start success."); + if (method.IsAsyncMethod()) + { + if (method.Invoke(job, @params) is Task task) + await task; + } + else + { + method.Invoke(job, @params); + } + _logger.LogInformation($"### job [{jobEntry.JobName}] invoke complete."); + jobEntry.NextUtcTime = new CronExpression(jobEntry.Cron).GetTimeAfter(DateTime.UtcNow); + if (!jobEntry.NextUtcTime.HasValue) + _logger.LogWarning($"### job [{jobEntry.JobName}] is stopped."); + } + } + catch (Exception e) + { + _logger.LogError($"### job [{jobEntry.JobName}] invoke fail : {e}."); + } + finally + { + jobEntry.CompleteRun(); + } + }); + } + } + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/JobTypeParser.cs b/src/ShardingCore/Jobs/JobTypeParser.cs new file mode 100644 index 00000000..c6e2802c --- /dev/null +++ b/src/ShardingCore/Jobs/JobTypeParser.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using ShardingCore.Jobs.Cron; +using ShardingCore.Jobs.Extensions; +using ShardingCore.Jobs.Impls; +using ShardingCore.Jobs.Impls.Attributes; + +namespace ShardingCore.Jobs +{ +/* +* @Author: xjm +* @Description: +* @Date: Wednesday, 06 January 2021 15:48:59 +* @Email: 326308290@qq.com +*/ + internal class JobTypeParser + { + private JobTypeParser(){} + + public static List Parse(Type type) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + if (!type.IsJobType()) + throw new NotSupportedException(type.FullName); + var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance); + var list = new List(); + foreach (var method in methods) + { + var jobRun = method.GetCustomAttribute(); + if ( jobRun!= null) + { + var jobEntry = new JobEntry() + { + BeginUtcTime = jobRun.BeginUtcTime, + Cron = jobRun.Cron, + JobName = jobRun.Name ?? (type.Name + "." + method.Name), + NextUtcTime = GetNextRunUtcTime(jobRun), + SkipIfRunning = jobRun.SkipIfRunning, + CreateFromServiceProvider = jobRun.CreateFromServiceProvider, + JobClass = type, + JobMethod = method + }; + list.Add(jobEntry); + } + } + + return list; + } + + /// + /// 判断时间是否满足启动时间如果满足就判断是需要在程序启动时就执行一次,如果需要就返回当前时间,如果不需要就按启动时间生成下次执行时间 + /// + /// + /// + private static DateTime? GetNextRunUtcTime(JobRunAttribute jobRun) + { + var utcNow = DateTime.UtcNow; + if (utcNow >= jobRun.BeginUtcTime) + { + if (jobRun.RunOnceOnStart) + { + return utcNow; + } + } + return new CronExpression(jobRun.Cron).GetTimeAfter(jobRun.BeginUtcTime); + + } + } +} \ No newline at end of file diff --git a/src/ShardingCore/Jobs/UtcTime.cs b/src/ShardingCore/Jobs/UtcTime.cs new file mode 100644 index 00000000..b8c8327d --- /dev/null +++ b/src/ShardingCore/Jobs/UtcTime.cs @@ -0,0 +1,25 @@ +using System; + +namespace ShardingCore.Jobs +{ +/* +* @Author: xjm +* @Description: +* @Date: Thursday, 07 January 2021 13:01:53 +* @Email: 326308290@qq.com +*/ + internal class UtcTime + { + private UtcTime(){} + private static readonly long UtcStartTicks = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks; + public static long CurrentTimeMillis() + { + return (DateTime.UtcNow.Ticks - UtcStartTicks) / 10000; + } + + public static long InputUtcTimeMillis(DateTime utcTime) + { + return (utcTime.Ticks - UtcStartTicks) / 10000; + } + } +} \ No newline at end of file diff --git a/src/ShardingCore/Sharding/Visitors/QueryableRouteShardingDataSourceDiscoverVisitor.cs b/src/ShardingCore/Sharding/Visitors/QueryableRouteShardingDataSourceDiscoverVisitor.cs index 7c51e203..e6b28f15 100644 --- a/src/ShardingCore/Sharding/Visitors/QueryableRouteShardingDataSourceDiscoverVisitor.cs +++ b/src/ShardingCore/Sharding/Visitors/QueryableRouteShardingDataSourceDiscoverVisitor.cs @@ -1,251 +1,252 @@ -using System; -using System.Collections; -using System.Linq; -using System.Linq.Expressions; -using ShardingCore.Core.VirtualDatabase; -using ShardingCore.Core.VirtualRoutes; -using ShardingCore.Exceptions; -using ShardingCore.Extensions; +//using System; +//using System.Collections; +//using System.Linq; +//using System.Linq.Expressions; +//using ShardingCore.Core.EntityMetadatas; +//using ShardingCore.Core.VirtualDatabase; +//using ShardingCore.Core.VirtualRoutes; +//using ShardingCore.Exceptions; +//using ShardingCore.Extensions; -namespace ShardingCore.Core.Internal.Visitors -{ -/* -* @Author: xjm -* @Description: -* @Date: Saturday, 06 February 2021 09:38:22 -* @Email: 326308290@qq.com -*/ - public class QueryableRouteShardingDataSourceDiscoverVisitor : ExpressionVisitor - { - private readonly ShardingEntityConfig _shardingEntityBaseType; - private readonly Func _shardingKeyConvert; - private readonly Func>> _keyToDataSourceWithFilter; - private Expression> _where = x => true; +//namespace ShardingCore.Core.Internal.Visitors +//{ +///* +//* @Author: xjm +//* @Description: +//* @Date: Saturday, 06 February 2021 09:38:22 +//* @Email: 326308290@qq.com +//*/ +// public class QueryableRouteShardingDataSourceDiscoverVisitor : ExpressionVisitor +// { +// private readonly EntityMetadata _entityMetadata; +// private readonly Func _shardingKeyConvert; +// private readonly Func>> _keyToDataSourceWithFilter; +// private Expression> _where = x => true; - public QueryableRouteShardingDataSourceDiscoverVisitor(ShardingEntityConfig shardingEntityBaseType, Func shardingKeyConvert, Func>> keyToDataSourceWithFilter) - { - _shardingEntityBaseType = shardingEntityBaseType; - _shardingKeyConvert = shardingKeyConvert; - _keyToDataSourceWithFilter = keyToDataSourceWithFilter; - } +// public QueryableRouteShardingDataSourceDiscoverVisitor(EntityMetadata entityMetadata, Func shardingKeyConvert, Func>> keyToDataSourceWithFilter) +// { +// _entityMetadata = entityMetadata; +// _shardingKeyConvert = shardingKeyConvert; +// _keyToDataSourceWithFilter = keyToDataSourceWithFilter; +// } - public Func GetDataSourceFilter() - { - return _where.Compile(); - } +// public Func GetDataSourceFilter() +// { +// return _where.Compile(); +// } - private bool IsShardingKey(Expression expression) - { - return expression is MemberExpression member - && member.Expression.Type == _shardingEntityBaseType.EntityType - && member.Member.Name == _shardingEntityBaseType.ShardingDataSourceField; - } - /// - /// 方法是否包含shardingKey - /// - /// - /// - private bool IsMethodWrapShardingKey(MethodCallExpression methodCallExpression) - { - if (methodCallExpression.Arguments.IsNotEmpty()) - { - for (int i = 0; i < methodCallExpression.Arguments.Count; i++) - { - var isShardingKey = methodCallExpression.Arguments[i] is MemberExpression member - && member.Expression.Type == _shardingEntityBaseType.EntityType - && member.Member.Name == _shardingEntityBaseType.ShardingTableField; - if (isShardingKey) return true; - } - } +// private bool IsShardingKey(Expression expression) +// { +// return expression is MemberExpression member +// && member.Expression.Type == _entityMetadata.EntityType +// && member.Member.Name == _entityMetadata.ShardingDataSourceProperty.Name; +// } +// /// +// /// 方法是否包含shardingKey +// /// +// /// +// /// +// private bool IsMethodWrapShardingKey(MethodCallExpression methodCallExpression) +// { +// if (methodCallExpression.Arguments.IsNotEmpty()) +// { +// for (int i = 0; i < methodCallExpression.Arguments.Count; i++) +// { +// var isShardingKey = methodCallExpression.Arguments[i] is MemberExpression member +// && member.Expression.Type == _entityMetadata.EntityType +// && member.Member.Name == _entityMetadata.ShardingDataSourceProperty.Name; +// if (isShardingKey) return true; +// } +// } - return false; - } - private bool IsConstantOrMember(Expression expression) - { - return expression is ConstantExpression - || (expression is MemberExpression member && (member.Expression is ConstantExpression || member.Expression is MemberExpression || member.Expression is MemberExpression)); - } +// return false; +// } +// private bool IsConstantOrMember(Expression expression) +// { +// return expression is ConstantExpression +// || (expression is MemberExpression member && (member.Expression is ConstantExpression || member.Expression is MemberExpression || member.Expression is MemberExpression)); +// } - private object GetFieldValue(Expression expression) - { - if (expression is ConstantExpression) - return (expression as ConstantExpression).Value; - if (expression is UnaryExpression) - { - UnaryExpression unary = expression as UnaryExpression; - LambdaExpression lambda = Expression.Lambda(unary.Operand); - Delegate fn = lambda.Compile(); - return fn.DynamicInvoke(null); - } +// private object GetFieldValue(Expression expression) +// { +// if (expression is ConstantExpression) +// return (expression as ConstantExpression).Value; +// if (expression is UnaryExpression) +// { +// UnaryExpression unary = expression as UnaryExpression; +// LambdaExpression lambda = Expression.Lambda(unary.Operand); +// Delegate fn = lambda.Compile(); +// return fn.DynamicInvoke(null); +// } - if (expression is MemberExpression member1Expression) - { - return Expression.Lambda(member1Expression).Compile().DynamicInvoke(); - } +// if (expression is MemberExpression member1Expression) +// { +// return Expression.Lambda(member1Expression).Compile().DynamicInvoke(); +// } - throw new ShardingKeyGetValueException("cant get value " + expression); - } +// throw new ShardingKeyGetValueException("cant get value " + expression); +// } - protected override Expression VisitMethodCall(MethodCallExpression node) - { - if (node.Method.Name == nameof(Queryable.Where)) - { - if (node.Arguments[1] is UnaryExpression unaryExpression) - { - if (unaryExpression.Operand is LambdaExpression lambdaExpression) - { - var newWhere = Resolve(lambdaExpression); - _where = _where.And(newWhere); - } - } - } +// protected override Expression VisitMethodCall(MethodCallExpression node) +// { +// if (node.Method.Name == nameof(Queryable.Where)) +// { +// if (node.Arguments[1] is UnaryExpression unaryExpression) +// { +// if (unaryExpression.Operand is LambdaExpression lambdaExpression) +// { +// var newWhere = Resolve(lambdaExpression); +// _where = _where.And(newWhere); +// } +// } +// } - return base.VisitMethodCall(node); - } +// return base.VisitMethodCall(node); +// } - private Expression> Resolve(Expression expression) - { - if (expression is LambdaExpression) - { - LambdaExpression lambda = expression as LambdaExpression; - expression = lambda.Body; - return Resolve(expression); - } +// private Expression> Resolve(Expression expression) +// { +// if (expression is LambdaExpression) +// { +// LambdaExpression lambda = expression as LambdaExpression; +// expression = lambda.Body; +// return Resolve(expression); +// } - if (expression is BinaryExpression binaryExpression) //解析二元运算符 - { - return ParseGetWhere(binaryExpression); - } +// if (expression is BinaryExpression binaryExpression) //解析二元运算符 +// { +// return ParseGetWhere(binaryExpression); +// } - if (expression is UnaryExpression) //解析一元运算符 - { - UnaryExpression unary = expression as UnaryExpression; - if (unary.Operand is MethodCallExpression methodCall1Expression) - { - // return ResolveLinqToObject(unary.Operand, false); - return ResolveInFunc(methodCall1Expression, unary.NodeType != ExpressionType.Not); - } - } +// if (expression is UnaryExpression) //解析一元运算符 +// { +// UnaryExpression unary = expression as UnaryExpression; +// if (unary.Operand is MethodCallExpression methodCall1Expression) +// { +// // return ResolveLinqToObject(unary.Operand, false); +// return ResolveInFunc(methodCall1Expression, unary.NodeType != ExpressionType.Not); +// } +// } - if (expression is MethodCallExpression methodCallExpression) //解析扩展方法 - { - return ResolveInFunc(methodCallExpression, true); - } - return o => true; - } +// if (expression is MethodCallExpression methodCallExpression) //解析扩展方法 +// { +// return ResolveInFunc(methodCallExpression, true); +// } +// return o => true; +// } - private Expression> ResolveInFunc(MethodCallExpression methodCallExpression, bool @in) - { - if (methodCallExpression.IsEnumerableContains(methodCallExpression.Method.Name) && IsMethodWrapShardingKey(methodCallExpression)) - { - object arrayObject = null; - if (methodCallExpression.Object != null) - { - if (methodCallExpression.Object is MemberExpression member1Expression) - { - arrayObject = Expression.Lambda(member1Expression).Compile().DynamicInvoke(); - } - else if (methodCallExpression.Object is ListInitExpression member2Expression) - { - arrayObject = Expression.Lambda(member2Expression).Compile().DynamicInvoke(); - } - } - else if (methodCallExpression.Arguments[0] is MemberExpression member2Expression) - { - arrayObject = Expression.Lambda(member2Expression).Compile().DynamicInvoke(); - } - else if (methodCallExpression.Arguments[0] is NewArrayExpression member3Expression) - { - arrayObject = Expression.Lambda(member3Expression).Compile().DynamicInvoke(); - } +// private Expression> ResolveInFunc(MethodCallExpression methodCallExpression, bool @in) +// { +// if (methodCallExpression.IsEnumerableContains(methodCallExpression.Method.Name) && IsMethodWrapShardingKey(methodCallExpression)) +// { +// object arrayObject = null; +// if (methodCallExpression.Object != null) +// { +// if (methodCallExpression.Object is MemberExpression member1Expression) +// { +// arrayObject = Expression.Lambda(member1Expression).Compile().DynamicInvoke(); +// } +// else if (methodCallExpression.Object is ListInitExpression member2Expression) +// { +// arrayObject = Expression.Lambda(member2Expression).Compile().DynamicInvoke(); +// } +// } +// else if (methodCallExpression.Arguments[0] is MemberExpression member2Expression) +// { +// arrayObject = Expression.Lambda(member2Expression).Compile().DynamicInvoke(); +// } +// else if (methodCallExpression.Arguments[0] is NewArrayExpression member3Expression) +// { +// arrayObject = Expression.Lambda(member3Expression).Compile().DynamicInvoke(); +// } - if (arrayObject != null) - { - var enumerable = (IEnumerable) arrayObject; - Expression> contains = x => false; - if (!@in) - contains = x => true; - foreach (var item in enumerable) - { - var keyValue = _shardingKeyConvert(item); - var eq = _keyToDataSourceWithFilter(keyValue, @in ? ShardingOperatorEnum.Equal : ShardingOperatorEnum.NotEqual); - if (@in) - contains = contains.Or(eq); - else - contains = contains.And(eq); - } +// if (arrayObject != null) +// { +// var enumerable = (IEnumerable) arrayObject; +// Expression> contains = x => false; +// if (!@in) +// contains = x => true; +// foreach (var item in enumerable) +// { +// var keyValue = _shardingKeyConvert(item); +// var eq = _keyToDataSourceWithFilter(keyValue, @in ? ShardingOperatorEnum.Equal : ShardingOperatorEnum.NotEqual); +// if (@in) +// contains = contains.Or(eq); +// else +// contains = contains.And(eq); +// } - return contains; - } - } +// return contains; +// } +// } - return x => true; - } +// return x => true; +// } - private Expression> ParseGetWhere(BinaryExpression binaryExpression) - { - Expression> left = x => true; - Expression> right = x => true; +// private Expression> ParseGetWhere(BinaryExpression binaryExpression) +// { +// Expression> left = x => true; +// Expression> right = x => true; - //递归获取 - if (binaryExpression.Left is BinaryExpression) - left = ParseGetWhere(binaryExpression.Left as BinaryExpression); - if (binaryExpression.Left is MethodCallExpression methodCallExpression) - left = Resolve(methodCallExpression); +// //递归获取 +// if (binaryExpression.Left is BinaryExpression) +// left = ParseGetWhere(binaryExpression.Left as BinaryExpression); +// if (binaryExpression.Left is MethodCallExpression methodCallExpression) +// left = Resolve(methodCallExpression); - if (binaryExpression.Left is UnaryExpression unaryExpression) - left = Resolve(unaryExpression); +// if (binaryExpression.Left is UnaryExpression unaryExpression) +// left = Resolve(unaryExpression); - if (binaryExpression.Right is BinaryExpression) - right = ParseGetWhere(binaryExpression.Right as BinaryExpression); +// if (binaryExpression.Right is BinaryExpression) +// right = ParseGetWhere(binaryExpression.Right as BinaryExpression); - //组合 - if (binaryExpression.NodeType == ExpressionType.AndAlso) - { - return left.And(right); - } - else if (binaryExpression.NodeType == ExpressionType.OrElse) - { - return left.Or(right); - } - //单个 - else - { - bool paramterAtLeft; - object value = null; +// //组合 +// if (binaryExpression.NodeType == ExpressionType.AndAlso) +// { +// return left.And(right); +// } +// else if (binaryExpression.NodeType == ExpressionType.OrElse) +// { +// return left.Or(right); +// } +// //单个 +// else +// { +// bool paramterAtLeft; +// object value = null; - if (IsShardingKey(binaryExpression.Left) && IsConstantOrMember(binaryExpression.Right)) - { - paramterAtLeft = true; - value = GetFieldValue(binaryExpression.Right); - } - else if (IsConstantOrMember(binaryExpression.Left) && IsShardingKey(binaryExpression.Right)) - { - paramterAtLeft = false; - value = GetFieldValue(binaryExpression.Left); - } - else - return x => true; +// if (IsShardingKey(binaryExpression.Left) && IsConstantOrMember(binaryExpression.Right)) +// { +// paramterAtLeft = true; +// value = GetFieldValue(binaryExpression.Right); +// } +// else if (IsConstantOrMember(binaryExpression.Left) && IsShardingKey(binaryExpression.Right)) +// { +// paramterAtLeft = false; +// value = GetFieldValue(binaryExpression.Left); +// } +// else +// return x => true; - var op = binaryExpression.NodeType switch - { - ExpressionType.GreaterThan => paramterAtLeft ? ShardingOperatorEnum.GreaterThan : ShardingOperatorEnum.LessThan, - ExpressionType.GreaterThanOrEqual => paramterAtLeft ? ShardingOperatorEnum.GreaterThanOrEqual : ShardingOperatorEnum.LessThanOrEqual, - ExpressionType.LessThan => paramterAtLeft ? ShardingOperatorEnum.LessThan : ShardingOperatorEnum.GreaterThan, - ExpressionType.LessThanOrEqual => paramterAtLeft ? ShardingOperatorEnum.LessThanOrEqual : ShardingOperatorEnum.GreaterThanOrEqual, - ExpressionType.Equal => ShardingOperatorEnum.Equal, - ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual, - _ => ShardingOperatorEnum.UnKnown - }; +// var op = binaryExpression.NodeType switch +// { +// ExpressionType.GreaterThan => paramterAtLeft ? ShardingOperatorEnum.GreaterThan : ShardingOperatorEnum.LessThan, +// ExpressionType.GreaterThanOrEqual => paramterAtLeft ? ShardingOperatorEnum.GreaterThanOrEqual : ShardingOperatorEnum.LessThanOrEqual, +// ExpressionType.LessThan => paramterAtLeft ? ShardingOperatorEnum.LessThan : ShardingOperatorEnum.GreaterThan, +// ExpressionType.LessThanOrEqual => paramterAtLeft ? ShardingOperatorEnum.LessThanOrEqual : ShardingOperatorEnum.GreaterThanOrEqual, +// ExpressionType.Equal => ShardingOperatorEnum.Equal, +// ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual, +// _ => ShardingOperatorEnum.UnKnown +// }; - if (value == null) - return x => true; +// if (value == null) +// return x => true; - var keyValue = _shardingKeyConvert(value); - return _keyToDataSourceWithFilter(keyValue, op); - } - } - } -} \ No newline at end of file +// var keyValue = _shardingKeyConvert(value); +// return _keyToDataSourceWithFilter(keyValue, op); +// } +// } +// } +//} \ No newline at end of file diff --git a/src/ShardingCore/TableCreator/IShardingTableCreator.cs b/src/ShardingCore/TableCreator/IShardingTableCreator.cs index b0782b20..c170035b 100644 --- a/src/ShardingCore/TableCreator/IShardingTableCreator.cs +++ b/src/ShardingCore/TableCreator/IShardingTableCreator.cs @@ -12,16 +12,15 @@ namespace ShardingCore.TableCreator * @Date: Monday, 21 December 2020 11:22:08 * @Email: 326308290@qq.com */ - public interface IShardingTableCreator where TShardingDbContext : DbContext, IShardingDbContext + public interface IShardingTableCreator { - /// /// 创建表 /// /// /// /// - void CreateTable(string dataSourceName, string tail) where T : class, IShardingTable; + void CreateTable(string dataSourceName, string tail) where T : class; /// /// 创建表 /// @@ -31,4 +30,8 @@ namespace ShardingCore.TableCreator /// void CreateTable(string dataSourceName, Type shardingEntityType, string tail); } + public interface IShardingTableCreator: IShardingTableCreator where TShardingDbContext : DbContext, IShardingDbContext + { + + } } \ No newline at end of file diff --git a/src/ShardingCore/TableCreator/ShardingTableCreator.cs b/src/ShardingCore/TableCreator/ShardingTableCreator.cs index c36f305b..119322d9 100644 --- a/src/ShardingCore/TableCreator/ShardingTableCreator.cs +++ b/src/ShardingCore/TableCreator/ShardingTableCreator.cs @@ -44,7 +44,7 @@ namespace ShardingCore.TableCreator _routeTailFactory = routeTailFactory; } - public void CreateTable< T>(string dataSourceName, string tail) where T : class, IShardingTable + public void CreateTable(string dataSourceName, string tail) where T : class { CreateTable(dataSourceName,typeof(T), tail); } diff --git a/src/ShardingCore/Utils/ShardingUtil.cs b/src/ShardingCore/Utils/ShardingUtil.cs index 55409539..bef33131 100644 --- a/src/ShardingCore/Utils/ShardingUtil.cs +++ b/src/ShardingCore/Utils/ShardingUtil.cs @@ -23,24 +23,24 @@ namespace ShardingCore.Utils */ public class ShardingUtil { - - /// - /// ֿ·ɹ - /// - /// - /// - /// - /// - /// - /// - public static Func GetRouteDataSourceFilter(IQueryable queryable, ShardingEntityConfig shardingEntityBaseType, Func shardingKeyConvert, Func>> keyToTailExpression) - { - QueryableRouteShardingDataSourceDiscoverVisitor visitor = new QueryableRouteShardingDataSourceDiscoverVisitor(shardingEntityBaseType, shardingKeyConvert, keyToTailExpression); - visitor.Visit(queryable.Expression); + ///// + ///// ֿ·ɹ + ///// + ///// + ///// + ///// + ///// + ///// + ///// + //public static Func GetRouteDataSourceFilter(IQueryable queryable, EntityMetadata entityMetadata, Func shardingKeyConvert, Func>> keyToTailExpression) + //{ + // QueryableRouteShardingDataSourceDiscoverVisitor visitor = new QueryableRouteShardingDataSourceDiscoverVisitor(entityMetadata, shardingKeyConvert, keyToTailExpression); - return visitor.GetDataSourceFilter(); - } + // visitor.Visit(queryable.Expression); + + // return visitor.GetDataSourceFilter(); + //} /// /// ֱ·ɹ /// diff --git a/src/ShardingCore/VirtualRoutes/Abstractions/AbstractShardingTimeKeyDateTimeVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Abstractions/AbstractShardingTimeKeyDateTimeVirtualTableRoute.cs index fcad5aa9..ee50b1a1 100644 --- a/src/ShardingCore/VirtualRoutes/Abstractions/AbstractShardingTimeKeyDateTimeVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Abstractions/AbstractShardingTimeKeyDateTimeVirtualTableRoute.cs @@ -15,7 +15,7 @@ namespace ShardingCore.VirtualRoutes.Abstractions /// time type is date time ///
/// - public abstract class AbstractShardingTimeKeyDateTimeVirtualTableRoute : AbstractShardingOperatorVirtualTableRoute where T : class, IShardingTable + public abstract class AbstractShardingTimeKeyDateTimeVirtualTableRoute : AbstractShardingOperatorVirtualTableRoute where T : class { /// /// how convert object to date time diff --git a/src/ShardingCore/VirtualRoutes/Abstractions/AbstractShardingTimeKeyLongVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Abstractions/AbstractShardingTimeKeyLongVirtualTableRoute.cs index efb5fc04..7c13ac6a 100644 --- a/src/ShardingCore/VirtualRoutes/Abstractions/AbstractShardingTimeKeyLongVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Abstractions/AbstractShardingTimeKeyLongVirtualTableRoute.cs @@ -14,7 +14,7 @@ namespace ShardingCore.VirtualRoutes.Abstractions /// sharding table route by time stamp (ms) /// /// entity - public abstract class AbstractShardingTimeKeyLongVirtualTableRoute : AbstractShardingOperatorVirtualTableRoute where T : class, IShardingTable + public abstract class AbstractShardingTimeKeyLongVirtualTableRoute : AbstractShardingOperatorVirtualTableRoute where T : class { /// /// how convert object to long diff --git a/src/ShardingCore/VirtualRoutes/Days/AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Days/AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute.cs index 99b569f5..dc3ef530 100644 --- a/src/ShardingCore/VirtualRoutes/Days/AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Days/AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute.cs @@ -3,7 +3,14 @@ using System.Collections.Generic; using System.Linq.Expressions; using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualTables; using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Extensions; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls.Attributes; +using ShardingCore.TableCreator; using ShardingCore.VirtualRoutes.Abstractions; namespace ShardingCore.VirtualRoutes.Days @@ -14,7 +21,7 @@ namespace ShardingCore.VirtualRoutes.Days * @Date: Wednesday, 27 January 2021 08:41:05 * @Email: 326308290@qq.com */ - public abstract class AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute:AbstractShardingTimeKeyDateTimeVirtualTableRoute where T:class,IShardingTable + public abstract class AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute:AbstractShardingTimeKeyDateTimeVirtualTableRoute where T:class { /// /// begin time use fixed time eg.new DateTime(20xx,xx,xx) @@ -77,6 +84,58 @@ namespace ShardingCore.VirtualRoutes.Days } } } + /// + /// 每天5点执行,启动的时候执行以下 + /// + [JobRun(Name = "定时创建表", Cron = "0 0 5 * * ?", RunOnceOnStart = true)] + public virtual void AutoShardingTableCreate() + { + var virtualDataSource = (IVirtualDataSource)ShardingContainer.GetService(typeof(IVirtualDataSource<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var tableCreator = (IShardingTableCreator)ShardingContainer.GetService(typeof(IShardingTableCreator<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(1); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + try + { + tableCreator.CreateTable(virtualDataSource.DefaultDataSourceName, typeof(T), tail); + } + catch (Exception e) + { + //ignore + } + } + /// + /// 每天晚上23点59分执行 + /// + [JobRun(Name = "定时添加虚拟表", Cron = "0 55 23 * * ?")] + public virtual void AutoShardingTableAdd() + { + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(1); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + + virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, tail)); + + + } + + public virtual string JobName => + $"{EntityMetadata?.ShardingDbContextType?.Name}:{EntityMetadata?.EntityType?.Name}"; + + public virtual bool StartJob() + { + return false; + } } } \ No newline at end of file diff --git a/src/ShardingCore/VirtualRoutes/Days/AbstractSimpleShardingDayKeyLongVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Days/AbstractSimpleShardingDayKeyLongVirtualTableRoute.cs index 66d0444d..e4e36a9c 100644 --- a/src/ShardingCore/VirtualRoutes/Days/AbstractSimpleShardingDayKeyLongVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Days/AbstractSimpleShardingDayKeyLongVirtualTableRoute.cs @@ -1,10 +1,19 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualTables; using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Extensions; using ShardingCore.Helpers; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls.Attributes; +using ShardingCore.Sharding.Abstractions; +using ShardingCore.TableCreator; using ShardingCore.VirtualRoutes.Abstractions; namespace ShardingCore.VirtualRoutes.Days @@ -15,7 +24,7 @@ namespace ShardingCore.VirtualRoutes.Days * @Date: Wednesday, 27 January 2021 08:56:38 * @Email: 326308290@qq.com */ - public abstract class AbstractSimpleShardingDayKeyLongVirtualTableRoute:AbstractShardingTimeKeyLongVirtualTableRoute where T:class,IShardingTable + public abstract class AbstractSimpleShardingDayKeyLongVirtualTableRoute:AbstractShardingTimeKeyLongVirtualTableRoute,IJob where T:class { public abstract DateTime GetBeginTime(); public override List GetAllTails() @@ -72,5 +81,58 @@ namespace ShardingCore.VirtualRoutes.Days } } } + + /// + /// 每天5点执行,启动的时候执行以下 + /// + [JobRun(Name = "定时创建表", Cron = "0 0 5 * * ?", RunOnceOnStart = true)] + public virtual void AutoShardingTableCreate() + { + var virtualDataSource = (IVirtualDataSource)ShardingContainer.GetService(typeof(IVirtualDataSource<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var tableCreator = (IShardingTableCreator)ShardingContainer.GetService(typeof(IShardingTableCreator<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(1); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + try + { + tableCreator.CreateTable(virtualDataSource.DefaultDataSourceName, typeof(T), tail); + } + catch (Exception e) + { + //ignore + } + } + /// + /// 每天晚上23点59分执行 + /// + [JobRun(Name = "定时添加虚拟表", Cron = "0 55 23 * * ?")] + public virtual void AutoShardingTableAdd() + { + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(1); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + + virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, tail)); + + + } + + public virtual string JobName => + $"{EntityMetadata?.ShardingDbContextType?.Name}:{EntityMetadata?.EntityType?.Name}"; + + public virtual bool StartJob() + { + return false; + } } } \ No newline at end of file diff --git a/src/ShardingCore/VirtualRoutes/Mods/AbstractSimpleShardingModKeyIntVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Mods/AbstractSimpleShardingModKeyIntVirtualTableRoute.cs index 4777f577..830b294e 100644 --- a/src/ShardingCore/VirtualRoutes/Mods/AbstractSimpleShardingModKeyIntVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Mods/AbstractSimpleShardingModKeyIntVirtualTableRoute.cs @@ -19,7 +19,7 @@ namespace ShardingCore.VirtualRoutes.Mods /// 分表字段为int的取模分表 /// /// - public abstract class AbstractSimpleShardingModKeyIntVirtualTableRoute: AbstractShardingOperatorVirtualTableRoute where T:class,IShardingTable + public abstract class AbstractSimpleShardingModKeyIntVirtualTableRoute: AbstractShardingOperatorVirtualTableRoute where T:class { protected readonly int Mod; protected readonly int TailLength; diff --git a/src/ShardingCore/VirtualRoutes/Mods/AbstractSimpleShardingModKeyStringVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Mods/AbstractSimpleShardingModKeyStringVirtualTableRoute.cs index 367a702f..a97a3769 100644 --- a/src/ShardingCore/VirtualRoutes/Mods/AbstractSimpleShardingModKeyStringVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Mods/AbstractSimpleShardingModKeyStringVirtualTableRoute.cs @@ -4,9 +4,16 @@ using System.Linq; using System.Linq.Expressions; using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualTables; using ShardingCore.Core.VirtualRoutes; using ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions; +using ShardingCore.Extensions; using ShardingCore.Helpers; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls.Attributes; +using ShardingCore.TableCreator; namespace ShardingCore.VirtualRoutes.Mods { @@ -20,7 +27,7 @@ namespace ShardingCore.VirtualRoutes.Mods /// 分表字段为string的取模分表 /// /// - public abstract class AbstractSimpleShardingModKeyStringVirtualTableRoute: AbstractShardingOperatorVirtualTableRoute where T:class,IShardingTable + public abstract class AbstractSimpleShardingModKeyStringVirtualTableRoute: AbstractShardingOperatorVirtualTableRoute where T:class { protected readonly int Mod; protected readonly int TailLength; diff --git a/src/ShardingCore/VirtualRoutes/Months/AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Months/AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute.cs index 42e61f42..404ce157 100644 --- a/src/ShardingCore/VirtualRoutes/Months/AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Months/AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute.cs @@ -3,8 +3,15 @@ using System.Collections.Generic; using System.Linq.Expressions; using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualTables; using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Extensions; using ShardingCore.Helpers; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls.Attributes; +using ShardingCore.TableCreator; using ShardingCore.VirtualRoutes.Abstractions; namespace ShardingCore.VirtualRoutes.Months @@ -15,7 +22,7 @@ namespace ShardingCore.VirtualRoutes.Months * @Date: Wednesday, 27 January 2021 12:27:09 * @Email: 326308290@qq.com */ - public abstract class AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute : AbstractShardingTimeKeyDateTimeVirtualTableRoute where T : class, IShardingTable + public abstract class AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute : AbstractShardingTimeKeyDateTimeVirtualTableRoute,IJob where T : class { public abstract DateTime GetBeginTime(); public override List GetAllTails() @@ -69,6 +76,61 @@ namespace ShardingCore.VirtualRoutes.Months } } } + /// + /// 每个月20号5点会创建对应的表 + /// + [JobRun(Name = "定时创建表", Cron = "0 0 5 20 * ?", RunOnceOnStart = true)] + public virtual void AutoShardingTableCreate() + { + + var virtualDataSource = (IVirtualDataSource)ShardingContainer.GetService(typeof(IVirtualDataSource<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var tableCreator = (IShardingTableCreator)ShardingContainer.GetService(typeof(IShardingTableCreator<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(3); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + try + { + tableCreator.CreateTable(virtualDataSource.DefaultDataSourceName, typeof(T), tail); + } + catch (Exception e) + { + //ignore + } + } + /// + /// 每月最后一天需要程序自己判断是不是最后一天 + /// + [JobRun(Name = "定时添加虚拟表", Cron = "0 55 23 28,29,30,31 * ?")] + public virtual void AutoShardingTableAdd() + { + var nowDateTime = DateTime.Now.Date; + var lastMonthDay = ShardingCoreHelper.GetNextMonthFirstDay(nowDateTime).AddDays(-1); + if (lastMonthDay.Date != nowDateTime) + return; + + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(7); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, tail)); + } + + public virtual string JobName => + $"{EntityMetadata?.ShardingDbContextType?.Name}:{EntityMetadata?.EntityType?.Name}"; + + public virtual bool StartJob() + { + return false; + } } } \ No newline at end of file diff --git a/src/ShardingCore/VirtualRoutes/Months/AbstractSimpleShardingMonthKeyLongVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Months/AbstractSimpleShardingMonthKeyLongVirtualTableRoute.cs index a41b2e76..7760e5a3 100644 --- a/src/ShardingCore/VirtualRoutes/Months/AbstractSimpleShardingMonthKeyLongVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Months/AbstractSimpleShardingMonthKeyLongVirtualTableRoute.cs @@ -3,8 +3,15 @@ using System.Collections.Generic; using System.Linq.Expressions; using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualTables; using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Extensions; using ShardingCore.Helpers; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls.Attributes; +using ShardingCore.TableCreator; using ShardingCore.VirtualRoutes.Abstractions; namespace ShardingCore.VirtualRoutes.Months @@ -15,7 +22,7 @@ namespace ShardingCore.VirtualRoutes.Months * @Date: Wednesday, 27 January 2021 13:09:52 * @Email: 326308290@qq.com */ - public abstract class AbstractSimpleShardingMonthKeyLongVirtualTableRoute:AbstractShardingTimeKeyLongVirtualTableRoute where T:class,IShardingTable + public abstract class AbstractSimpleShardingMonthKeyLongVirtualTableRoute:AbstractShardingTimeKeyLongVirtualTableRoute,IJob where T:class { public abstract DateTime GetBeginTime(); public override List GetAllTails() @@ -72,6 +79,61 @@ namespace ShardingCore.VirtualRoutes.Months } } } + /// + /// 每个月20号5点会创建对应的表 + /// + [JobRun(Name = "定时创建表", Cron = "0 0 5 20 * ?", RunOnceOnStart = true)] + public virtual void AutoShardingTableCreate() + { + + var virtualDataSource = (IVirtualDataSource)ShardingContainer.GetService(typeof(IVirtualDataSource<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var tableCreator = (IShardingTableCreator)ShardingContainer.GetService(typeof(IShardingTableCreator<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(3); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + try + { + tableCreator.CreateTable(virtualDataSource.DefaultDataSourceName, typeof(T), tail); + } + catch (Exception e) + { + //ignore + } + } + /// + /// 每月最后一天需要程序自己判断是不是最后一天 + /// + [JobRun(Name = "定时添加虚拟表", Cron = "0 55 23 28,29,30,31 * ?")] + public virtual void AutoShardingTableAdd() + { + var nowDateTime = DateTime.Now.Date; + var lastMonthDay = ShardingCoreHelper.GetNextMonthFirstDay(nowDateTime).AddDays(-1); + if (lastMonthDay.Date != nowDateTime) + return; + + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(7); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, tail)); + } + + public virtual string JobName => + $"{EntityMetadata?.ShardingDbContextType?.Name}:{EntityMetadata?.EntityType?.Name}"; + + public virtual bool StartJob() + { + return false; + } } } \ No newline at end of file diff --git a/src/ShardingCore/VirtualRoutes/Weeks/AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Weeks/AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute.cs index 8709eb25..91378772 100644 --- a/src/ShardingCore/VirtualRoutes/Weeks/AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Weeks/AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute.cs @@ -3,8 +3,15 @@ using System.Collections.Generic; using System.Linq.Expressions; using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualTables; using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Extensions; using ShardingCore.Helpers; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls.Attributes; +using ShardingCore.TableCreator; using ShardingCore.VirtualRoutes.Abstractions; namespace ShardingCore.VirtualRoutes.Weeks @@ -15,7 +22,7 @@ namespace ShardingCore.VirtualRoutes.Weeks * @Date: Wednesday, 27 January 2021 12:40:27 * @Email: 326308290@qq.com */ - public abstract class AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute : AbstractShardingTimeKeyDateTimeVirtualTableRoute where T : class, IShardingTable + public abstract class AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute : AbstractShardingTimeKeyDateTimeVirtualTableRoute,IJob where T : class { public abstract DateTime GetBeginTime(); public override List GetAllTails() @@ -71,6 +78,55 @@ namespace ShardingCore.VirtualRoutes.Weeks } } } + /// + /// 每周五 5点 + /// + [JobRun(Name = "定时创建表", Cron = "0 0 5 ? * 6", RunOnceOnStart = true)] + public virtual void AutoShardingTableCreate() + { + + var virtualDataSource = (IVirtualDataSource)ShardingContainer.GetService(typeof(IVirtualDataSource<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var tableCreator = (IShardingTableCreator)ShardingContainer.GetService(typeof(IShardingTableCreator<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(3); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + try + {tableCreator.CreateTable(virtualDataSource.DefaultDataSourceName, typeof(T), tail); + } + catch (Exception e) + { + //ignore + } + } + /// + /// 每周末晚上23点55分添加数据 + /// + [JobRun(Name = "定时添加虚拟表", Cron = "0 55 23 ? * 1")] + public virtual void AutoShardingTableAdd() + { + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(3); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, tail)); + } + + public virtual string JobName => + $"{EntityMetadata?.ShardingDbContextType?.Name}:{EntityMetadata?.EntityType?.Name}"; + + public virtual bool StartJob() + { + return false; + } } } \ No newline at end of file diff --git a/src/ShardingCore/VirtualRoutes/Weeks/AbstractSimpleShardingWeekKeyLongVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Weeks/AbstractSimpleShardingWeekKeyLongVirtualTableRoute.cs index d6c8fb06..82334fe0 100644 --- a/src/ShardingCore/VirtualRoutes/Weeks/AbstractSimpleShardingWeekKeyLongVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Weeks/AbstractSimpleShardingWeekKeyLongVirtualTableRoute.cs @@ -3,8 +3,15 @@ using System.Collections.Generic; using System.Linq.Expressions; using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualTables; using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Extensions; using ShardingCore.Helpers; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls.Attributes; +using ShardingCore.TableCreator; using ShardingCore.VirtualRoutes.Abstractions; namespace ShardingCore.VirtualRoutes.Weeks @@ -15,7 +22,7 @@ namespace ShardingCore.VirtualRoutes.Weeks * @Date: Wednesday, 27 January 2021 13:12:50 * @Email: 326308290@qq.com */ - public abstract class AbstractSimpleShardingWeekKeyLongVirtualTableRoute : AbstractShardingTimeKeyLongVirtualTableRoute where T : class, IShardingTable + public abstract class AbstractSimpleShardingWeekKeyLongVirtualTableRoute : AbstractShardingTimeKeyLongVirtualTableRoute,IJob where T : class { public abstract DateTime GetBeginTime(); @@ -75,5 +82,55 @@ namespace ShardingCore.VirtualRoutes.Weeks } } } + /// + /// 每周五 5点 + /// + [JobRun(Name = "定时创建表", Cron = "0 0 5 ? * 6", RunOnceOnStart = true)] + public virtual void AutoShardingTableCreate() + { + + var virtualDataSource = (IVirtualDataSource)ShardingContainer.GetService(typeof(IVirtualDataSource<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var tableCreator = (IShardingTableCreator)ShardingContainer.GetService(typeof(IShardingTableCreator<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(3); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + try + { + tableCreator.CreateTable(virtualDataSource.DefaultDataSourceName, typeof(T), tail); + } + catch (Exception e) + { + //ignore + } + } + /// + /// 每周末晚上23点55分添加数据 + /// + [JobRun(Name = "定时添加虚拟表", Cron = "0 55 23 ? * 1")] + public virtual void AutoShardingTableAdd() + { + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(3); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, tail)); + } + + public virtual string JobName => + $"{EntityMetadata?.ShardingDbContextType?.Name}:{EntityMetadata?.EntityType?.Name}"; + + public virtual bool StartJob() + { + return false; + } } } \ No newline at end of file diff --git a/src/ShardingCore/VirtualRoutes/Years/AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Years/AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute.cs index 6ec4afb1..13fc454d 100644 --- a/src/ShardingCore/VirtualRoutes/Years/AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Years/AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute.cs @@ -3,7 +3,14 @@ using System.Collections.Generic; using System.Linq.Expressions; using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualTables; using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Extensions; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls.Attributes; +using ShardingCore.TableCreator; using ShardingCore.VirtualRoutes.Abstractions; namespace ShardingCore.VirtualRoutes.Years @@ -14,7 +21,7 @@ namespace ShardingCore.VirtualRoutes.Years * @Date: Wednesday, 27 January 2021 13:00:57 * @Email: 326308290@qq.com */ - public abstract class AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute : AbstractShardingTimeKeyDateTimeVirtualTableRoute where T : class, IShardingTable + public abstract class AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute : AbstractShardingTimeKeyDateTimeVirtualTableRoute,IJob where T : class { public abstract DateTime GetBeginTime(); public override List GetAllTails() @@ -67,5 +74,55 @@ namespace ShardingCore.VirtualRoutes.Years } } } + /// + /// 每年12月20号自动创建表 + /// + [JobRun(Name = "定时创建表", Cron = "0 0 5 20 12 ?", RunOnceOnStart = true)] + public virtual void AutoShardingTableCreate() + { + + var virtualDataSource = (IVirtualDataSource)ShardingContainer.GetService(typeof(IVirtualDataSource<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var tableCreator = (IShardingTableCreator)ShardingContainer.GetService(typeof(IShardingTableCreator<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddMonths(1); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + try + { + tableCreator.CreateTable(virtualDataSource.DefaultDataSourceName, typeof(T), tail); + } + catch (Exception e) + { + //ignore + } + } + /// + /// 每年12月最后一天添加下一年的数据 + /// + [JobRun(Name = "定时添加虚拟表", Cron = "0 55 23 31 12 ?")] + public virtual void AutoShardingTableAdd() + { + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(7); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, tail)); + } + + public virtual string JobName => + $"{EntityMetadata?.ShardingDbContextType?.Name}:{EntityMetadata?.EntityType?.Name}"; + + public virtual bool StartJob() + { + return false; + } } } \ No newline at end of file diff --git a/src/ShardingCore/VirtualRoutes/Years/AbstractSimpleShardingYearKeyLongVirtualTableRoute.cs b/src/ShardingCore/VirtualRoutes/Years/AbstractSimpleShardingYearKeyLongVirtualTableRoute.cs index e59be395..0e0d3fd1 100644 --- a/src/ShardingCore/VirtualRoutes/Years/AbstractSimpleShardingYearKeyLongVirtualTableRoute.cs +++ b/src/ShardingCore/VirtualRoutes/Years/AbstractSimpleShardingYearKeyLongVirtualTableRoute.cs @@ -3,8 +3,15 @@ using System.Collections.Generic; using System.Linq.Expressions; using ShardingCore.Core; using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.PhysicTables; +using ShardingCore.Core.VirtualDatabase.VirtualDataSources; +using ShardingCore.Core.VirtualDatabase.VirtualTables; using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Extensions; using ShardingCore.Helpers; +using ShardingCore.Jobs.Abstaractions; +using ShardingCore.Jobs.Impls.Attributes; +using ShardingCore.TableCreator; using ShardingCore.VirtualRoutes.Abstractions; namespace ShardingCore.VirtualRoutes.Years @@ -15,7 +22,7 @@ namespace ShardingCore.VirtualRoutes.Years * @Date: Wednesday, 27 January 2021 13:17:24 * @Email: 326308290@qq.com */ - public abstract class AbstractSimpleShardingYearKeyLongVirtualTableRoute : AbstractShardingTimeKeyLongVirtualTableRoute where T : class, IShardingTable + public abstract class AbstractSimpleShardingYearKeyLongVirtualTableRoute : AbstractShardingTimeKeyLongVirtualTableRoute,IJob where T : class { public abstract DateTime GetBeginTime(); public override List GetAllTails() @@ -72,5 +79,56 @@ namespace ShardingCore.VirtualRoutes.Years } } } + + /// + /// 每年12月20号自动创建表 + /// + [JobRun(Name = "定时创建表", Cron = "0 0 5 20 12 ?", RunOnceOnStart = true)] + public virtual void AutoShardingTableCreate() + { + + var virtualDataSource = (IVirtualDataSource)ShardingContainer.GetService(typeof(IVirtualDataSource<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var tableCreator = (IShardingTableCreator)ShardingContainer.GetService(typeof(IShardingTableCreator<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddMonths(1); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + try + { + tableCreator.CreateTable(virtualDataSource.DefaultDataSourceName, typeof(T), tail); + } + catch (Exception e) + { + //ignore + } + } + /// + /// 每年12月最后一天添加下一年的数据 + /// + [JobRun(Name = "定时添加虚拟表", Cron = "0 55 23 31 12 ?")] + public virtual void AutoShardingTableAdd() + { + var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType)); + var virtualTable = virtualTableManager.GetVirtualTable(typeof(T)); + if (virtualTable == null) + { + return; + } + var now = DateTime.Now.Date.AddDays(7); + var tail = virtualTable.GetVirtualRoute().ShardingKeyToTail(now); + virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, tail)); + } + + public virtual string JobName => + $"{EntityMetadata?.ShardingDbContextType?.Name}:{EntityMetadata?.EntityType?.Name}"; + + public virtual bool StartJob() + { + return false; + } } } \ No newline at end of file diff --git a/test/ShardingCore.Test50.MySql/Domain/Entities/SysUserMod.cs b/test/ShardingCore.Test50.MySql/Domain/Entities/SysUserMod.cs index 11d4c1f5..4cc6e931 100644 --- a/test/ShardingCore.Test50.MySql/Domain/Entities/SysUserMod.cs +++ b/test/ShardingCore.Test50.MySql/Domain/Entities/SysUserMod.cs @@ -13,7 +13,7 @@ namespace ShardingCore.Test50.MySql.Domain.Entities /// /// 用户Id用于分表 /// - [ShardingTableKey(TailPrefix = "_")] + [ShardingTableKey(TableSeparator = "_")] public string Id { get; set; } /// /// 用户名称 diff --git a/test/ShardingCore.Test50.MySql/Domain/Entities/SysUserRange.cs b/test/ShardingCore.Test50.MySql/Domain/Entities/SysUserRange.cs index 9250b5f5..a11e7368 100644 --- a/test/ShardingCore.Test50.MySql/Domain/Entities/SysUserRange.cs +++ b/test/ShardingCore.Test50.MySql/Domain/Entities/SysUserRange.cs @@ -13,7 +13,7 @@ // /// // /// 分表分库range切分 // /// -// [ShardingKey(TailPrefix = "_",AutoCreateTableOnStart = true)] +// [ShardingKey(TableSeparator = "_",AutoCreateTableOnStart = true)] // public string Id { get; set; } // /// // /// 姓名 diff --git a/test/ShardingCore.Test50.MySql/Startup.cs b/test/ShardingCore.Test50.MySql/Startup.cs index ea77a661..e1fb0a4d 100644 --- a/test/ShardingCore.Test50.MySql/Startup.cs +++ b/test/ShardingCore.Test50.MySql/Startup.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using ShardingCore.Bootstrapers; using ShardingCore.DbContexts.VirtualDbContexts; using ShardingCore.Extensions; using ShardingCore.Test50.MySql.Domain.Entities; diff --git a/test/ShardingCore.Test50/Domain/Entities/SysUserMod.cs b/test/ShardingCore.Test50/Domain/Entities/SysUserMod.cs index 3e1ca042..2d203d46 100644 --- a/test/ShardingCore.Test50/Domain/Entities/SysUserMod.cs +++ b/test/ShardingCore.Test50/Domain/Entities/SysUserMod.cs @@ -8,12 +8,13 @@ namespace ShardingCore.Test50.Domain.Entities * @Date: Thursday, 14 January 2021 15:36:43 * @Email: 326308290@qq.com */ - public class SysUserMod:IShardingTable + public class SysUserMod + //:IShardingTable { /// /// 用户Id用于分表 /// - [ShardingTableKey(TailPrefix = "_")] + //[ShardingTableKey(TableSeparator = "_")] public string Id { get; set; } /// /// 用户名称 diff --git a/test/ShardingCore.Test50/Shardings/SysUserModVirtualTableRoute.cs b/test/ShardingCore.Test50/Shardings/SysUserModVirtualTableRoute.cs index 8437a5f4..78eff5fe 100644 --- a/test/ShardingCore.Test50/Shardings/SysUserModVirtualTableRoute.cs +++ b/test/ShardingCore.Test50/Shardings/SysUserModVirtualTableRoute.cs @@ -1,3 +1,4 @@ +using ShardingCore.Core.EntityMetadatas; using ShardingCore.Test50.Domain.Entities; using ShardingCore.VirtualRoutes.Mods; @@ -17,5 +18,10 @@ namespace ShardingCore.Test50.Shardings { } + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.Id); + builder.TableSeparator("_"); + } } } \ No newline at end of file diff --git a/test/ShardingCore.Test50/Startup.cs b/test/ShardingCore.Test50/Startup.cs index 2d65c789..799330af 100644 --- a/test/ShardingCore.Test50/Startup.cs +++ b/test/ShardingCore.Test50/Startup.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using ShardingCore.Bootstrapers; #if EFCORE5SQLSERVER using ShardingCore.SqlServer; diff --git a/test/ShardingCore.Test50_2x/Domain/Entities/SysUserMod.cs b/test/ShardingCore.Test50_2x/Domain/Entities/SysUserMod.cs index cdb1eb32..80a70b77 100644 --- a/test/ShardingCore.Test50_2x/Domain/Entities/SysUserMod.cs +++ b/test/ShardingCore.Test50_2x/Domain/Entities/SysUserMod.cs @@ -13,7 +13,7 @@ namespace ShardingCore.Test50_2x.Domain.Entities /// /// 用户Id用于分表 /// - [ShardingTableKey(TailPrefix = "_")] + [ShardingTableKey(TableSeparator = "_")] public string Id { get; set; } /// /// 用户名称 diff --git a/test/ShardingCore.Test50_2x/Startup.cs b/test/ShardingCore.Test50_2x/Startup.cs index 607046e6..f84c6c2a 100644 --- a/test/ShardingCore.Test50_2x/Startup.cs +++ b/test/ShardingCore.Test50_2x/Startup.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using ShardingCore.Bootstrapers; using ShardingCore.EFCores; using ShardingCore.Test50_2x.Domain.Entities; using ShardingCore.Test50_2x.Shardings; diff --git a/test/ShardingCore.Test50_3x/Domain/Entities/SysUserMod.cs b/test/ShardingCore.Test50_3x/Domain/Entities/SysUserMod.cs index 3a132587..0b596e1f 100644 --- a/test/ShardingCore.Test50_3x/Domain/Entities/SysUserMod.cs +++ b/test/ShardingCore.Test50_3x/Domain/Entities/SysUserMod.cs @@ -13,7 +13,7 @@ namespace ShardingCore.Test50_3x.Domain.Entities /// /// 用户Id用于分表 /// - [ShardingTableKey(TailPrefix = "_")] + [ShardingTableKey(TableSeparator = "_")] public string Id { get; set; } /// /// 用户名称 diff --git a/test/ShardingCore.Test50_3x/Startup.cs b/test/ShardingCore.Test50_3x/Startup.cs index d1a3e9c1..0b272015 100644 --- a/test/ShardingCore.Test50_3x/Startup.cs +++ b/test/ShardingCore.Test50_3x/Startup.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using ShardingCore.Bootstrapers; using ShardingCore.Test50_3x.Domain.Entities; using ShardingCore.Test50_3x.Shardings;